Kundenansicht neu: vorübergehende Messung der iPhone-Höhe
Der Fix 0fa8fdb hat die zu hoch stehende Leiste am iPhone nicht
behoben. Damit wir nicht weiter raten: die Seite schickt am iPhone
Bildschirmwerte (Fenster, visualViewport, Bildschirm, Safe-Area,
vh-Einheiten, Lage der Leiste, Eingriffe von hoehe.js) an
/api/diag/hoehe, der Server schreibt sie als Zeile "[diag-hoehe]" ins
Log. Keine Inhalte, nur Pilot-Kunden (nicht die Mitarbeiter-Ansicht),
höchstens 80 Meldungen je Seite und 400 Zeilen je Kunde und Stunde.
Wird nach der Klärung wieder entfernt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
0fa8fdbbac
commit
cca29a4aef
7 files changed
+188
-4
No files matched your search
@@ -0,0 +1,75 @@
|
||||
/* VORÜBERGEHENDE MESSUNG (2026-10-29), wird nach der Klärung wieder entfernt: Am iPhone (App vom Home-Bildschirm) steht die Leiste zu hoch.
|
||||
Schickt Bildschirmwerte (Fenster, visualViewport, Bildschirm, Safe-Area, vh-Einheiten, Lage der Leiste, Zustand von hoehe.js) an
|
||||
/api/diag/hoehe; der Server schreibt sie ins Log (server/routes/diagHoehe.js). Keine Inhalte, keine Eingaben. Höchstens MAX Meldungen je Seite. */
|
||||
|
||||
const MAX = 80
|
||||
const ZIEL = '/api/diag/hoehe'
|
||||
const rund = (x) => (Number.isFinite(x) ? Math.round(x * 10) / 10 : null)
|
||||
|
||||
export function starteDiagHoehe({ win = window, doc = document, hoehe = null }) {
|
||||
const t0 = Date.now()
|
||||
let n = 0
|
||||
const probe = (stil) => {
|
||||
const el = doc.createElement('div')
|
||||
el.setAttribute('aria-hidden', 'true')
|
||||
el.style.cssText = `position:fixed;left:0;top:0;width:0;visibility:hidden;pointer-events:none;${stil}`
|
||||
doc.body.append(el)
|
||||
return el
|
||||
}
|
||||
const sa = probe('height:0;padding:env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left)')
|
||||
const vh = probe('height:100vh'); const dvh = probe('height:100dvh'); const svh = probe('height:100svh'); const lvh = probe('height:100lvh')
|
||||
const unten = probe('top:auto;bottom:0;height:1px') // wo „bottom: 0“ eines festen Elements wirklich landet
|
||||
|
||||
function messe(art, extra) {
|
||||
const vv = win.visualViewport
|
||||
const cs = win.getComputedStyle(sa)
|
||||
const leiste = doc.getElementById('kn-leiste')?.getBoundingClientRect()
|
||||
const huelle = doc.querySelector('.kn-huelle')?.getBoundingClientRect()
|
||||
const aktiv = doc.activeElement
|
||||
return {
|
||||
art, ms: Date.now() - t0, hash: win.location.hash,
|
||||
iw: win.innerWidth, ih: win.innerHeight, ow: win.outerWidth, oh: win.outerHeight,
|
||||
cw: doc.documentElement.clientWidth, ch: doc.documentElement.clientHeight, dh: doc.documentElement.scrollHeight,
|
||||
sw: win.screen?.width, sh: win.screen?.height, sah: win.screen?.availHeight, dpr: win.devicePixelRatio,
|
||||
vv: vv ? [rund(vv.width), rund(vv.height), rund(vv.offsetTop), rund(vv.pageTop), rund(vv.scale)] : null,
|
||||
sy: rund(win.scrollY),
|
||||
sa: [cs.paddingTop, cs.paddingRight, cs.paddingBottom, cs.paddingLeft].map((p) => rund(parseFloat(p))),
|
||||
vh: [vh.offsetHeight, dvh.offsetHeight, svh.offsetHeight, lvh.offsetHeight],
|
||||
unten: rund(unten.getBoundingClientRect().bottom),
|
||||
leiste: leiste ? [rund(leiste.top), rund(leiste.bottom), rund(leiste.height)] : null,
|
||||
huelle: huelle ? [rund(huelle.top), rund(huelle.height)] : null,
|
||||
hoehe: hoehe?.stand?.() ?? null,
|
||||
app: Boolean(win.matchMedia?.('(display-mode: standalone)').matches), ns: win.navigator?.standalone ?? null,
|
||||
ori: win.screen?.orientation?.type ?? null,
|
||||
fokus: aktiv && aktiv !== doc.body ? aktiv.tagName : null,
|
||||
tast: doc.body.classList.contains('kn-tastatur'),
|
||||
...(n === 0 ? { ua: String(win.navigator?.userAgent ?? '').slice(0, 200) } : {}),
|
||||
...(extra ?? {}),
|
||||
}
|
||||
}
|
||||
|
||||
function sende(art, extra) {
|
||||
if (n >= MAX) return
|
||||
let daten
|
||||
try { daten = messe(art, extra) } catch (fehler) { daten = { art, fehler: String(fehler?.message ?? fehler).slice(0, 200) } }
|
||||
n += 1
|
||||
try {
|
||||
win.fetch(ZIEL, { method: 'POST', credentials: 'same-origin', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(daten), keepalive: true }).catch(() => {})
|
||||
} catch { /* Messung darf nie stören */ }
|
||||
}
|
||||
|
||||
const spaeter = (fn, ms) => win.setTimeout(fn, ms)
|
||||
const entprellt = (art) => { let id = null; return () => { if (id) win.clearTimeout(id); id = spaeter(() => { id = null; sende(art) }, 250) } }
|
||||
sende('start')
|
||||
spaeter(() => sende('start+1s'), 1000)
|
||||
spaeter(() => sende('start+4s'), 4000)
|
||||
win.addEventListener('resize', entprellt('resize'))
|
||||
win.visualViewport?.addEventListener('resize', entprellt('vv-resize'))
|
||||
win.visualViewport?.addEventListener('scroll', entprellt('vv-scroll'))
|
||||
doc.addEventListener('focusin', (ev) => { if (ev.target?.matches?.('textarea, input')) sende('fokus') })
|
||||
doc.addEventListener('focusout', (ev) => { if (ev.target?.matches?.('textarea, input')) { spaeter(() => sende('unfokus+300ms'), 300); spaeter(() => sende('unfokus+1s'), 1000) } })
|
||||
doc.addEventListener('visibilitychange', () => { if (doc.visibilityState === 'visible') { sende('sichtbar'); spaeter(() => sende('sichtbar+1s'), 1000) } })
|
||||
win.addEventListener('hashchange', () => spaeter(() => sende('route'), 600))
|
||||
win.addEventListener('pageshow', (ev) => { if (ev.persisted) sende('pageshow') })
|
||||
return { sende }
|
||||
}
|
||||
+5
-2
@@ -23,9 +23,10 @@ export function haengt(zustand, { hoehe, breite, textfeldAktiv }) {
|
||||
|
||||
/**
|
||||
* Verdrahtung: merkt sich die volle Höhe (resize), prüft nach dem Verlassen eines Feldes (focusout), nach einem resize ohne Textfeld
|
||||
* und wenn die App wieder nach vorne kommt (visibilitychange), jeweils zu den Zeiten PRUEF_MS. Rückgabe { pruefe, stand, beende }.
|
||||
* und wenn die App wieder nach vorne kommt (visibilitychange), jeweils zu den Zeiten PRUEF_MS. onHeile({ vorher, nachher, voll, versuche }) nach
|
||||
* jedem Eingriff. Rückgabe { pruefe, stand, beende }.
|
||||
*/
|
||||
export function beobachteHoehe({ win = window, doc = document, element, aktiv = () => true, istTextfeld = () => false }) {
|
||||
export function beobachteHoehe({ win = window, doc = document, element, aktiv = () => true, istTextfeld = () => false, onHeile = () => {} }) {
|
||||
const messe = () => ({ hoehe: win.innerHeight, breite: win.innerWidth })
|
||||
let z = hoeheZustand(null, messe())
|
||||
let versuche = 0
|
||||
@@ -57,8 +58,10 @@ export function beobachteHoehe({ win = window, doc = document, element, aktiv =
|
||||
if (!haengt(z, { ...m, textfeldAktiv: istTextfeld(doc.activeElement) })) { merke(); return false }
|
||||
if (versuche >= MAX_VERSUCHE) return false
|
||||
versuche += 1
|
||||
const vorher = win.innerHeight
|
||||
heile()
|
||||
merke()
|
||||
try { onHeile({ vorher, nachher: win.innerHeight, voll: z.voll, versuche }) } catch { /* nur Beobachter */ }
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import * as routerStandard from './router.js'
|
||||
import { BEREICHE, FUNKTIONS_ETAGEN, hashFuerAnsicht, hashFuerRoute, elternVon, titelFuer, zeigtWiderrufsstreifen } from './routen.js'
|
||||
import { HANDY_ABFRAGE, baueNavigation, beobachteTastatur, istTextfeld, leisteSichtbar } from './leiste.js'
|
||||
import { beobachteHoehe } from './hoehe.js'
|
||||
import { starteDiagHoehe } from './diag-hoehe.js'
|
||||
import { baueEtage } from './etage.js'
|
||||
import { erzeugeZaehler } from './zaehler.js'
|
||||
import { baueStreifen, ladeFristen, ladeFristenNeu } from './fristen-streifen.js'
|
||||
@@ -279,7 +280,10 @@ export async function starteHuelle({ win = window, doc = document, api = apiStan
|
||||
else handyMq.addListener?.(beiMedien)
|
||||
beobachteTastatur({ win, doc, onChange: (z) => doc.body.classList.toggle('kn-tastatur', !leisteSichtbar(z)) })
|
||||
// iOS-App vom Home-Bildschirm: hängt nach der Tastatur die Fensterhöhe, steht die Leiste zu hoch (hoehe.js); dann einmal neu vermessen
|
||||
beobachteHoehe({ win, doc, element: doc.querySelector('.kn-huelle'), aktiv: () => istIos() && standalone() && istHandy(), istTextfeld })
|
||||
let diag = null
|
||||
const hoehe = beobachteHoehe({ win, doc, element: doc.querySelector('.kn-huelle'), aktiv: () => istIos() && standalone() && istHandy(), istTextfeld, onHeile: (werte) => diag?.sende('heile', werte) })
|
||||
// VORÜBERGEHENDE MESSUNG (2026-10-29, diag-hoehe.js): Bildschirmwerte am iPhone ins Server-Log, wird nach der Klärung entfernt
|
||||
if (istIos() && istHandy() && !viewAs) diag = starteDiagHoehe({ win, doc, hoehe })
|
||||
|
||||
// 7. Fristen und Zähler laden parallel und brechen den Start nie ab
|
||||
ladeFristen(api).then((daten) => { fristen = daten; streifen.setze(daten); aktualisiereKonto() })
|
||||
|
||||
Reference in new issue
Block a user