Handy-App reparieren: Zugangsseite war nicht installierbar + Knopf ohne Rueckmeldung
Nutzer-Report 19.08.2026: "komm auf dem Handy nicht rein, hab es installiert und klappt nicht" / "er nimmt meinen Code nicht an". Hauptursache war ein veralteter Zugangscode (nur Doku, kein Code) -- beim Nachpruefen kamen aber drei echte Fehler auf der Zugangsseite zutage, alle nur auf dem Handy spuerbar: 1. gate.html war NICHT installierbar. Ohne gueltige Sitzung leitet gate.js jeden Aufruf hierher um -- es ist also der Bildschirm, von dem aus man die App installiert. Genau dort fehlten Manifest-Verweis und Service-Worker-Registrierung, die Chrome fuer eine echte App verlangt (index.html hatte beides laengst, gate.html laedt main.js bewusst nicht). Wer vor dem ersten Login installierte, bekam nur eine leere Verknuepfung. Exakt derselbe Fehler wie am 07.08.2026 in VanVans Shop, hier nie aufgefallen. 2. Der "Oeffnen"-Knopf gab keinerlei Rueckmeldung und die Anfrage hatte kein Zeitlimit. Bleibt im Mobilfunknetz eine Antwort aus, haengt fetch() unbegrenzt -- es sieht aus, als sei der Tap nicht angekommen. Jetzt: Knopf sperrt sich sofort und zeigt "Wird geprueft...", Abbruch nach 10s mit klarer Meldung (AbortController), Doppel-Tap ignoriert. Letzteres ist hier besonders wichtig, weil server/gate.js nach 5 Fehlversuchen die IP fuer 15 Minuten sperrt -- Doppel-Taps zaehlten bisher mit. 3. apple-touch-icon fehlte im GESAMTEN Projekt. iOS ignoriert das Manifest fuer das Startbildschirm-Symbol und liest nur dieses Tag -- auf dem iPhone gab es deshalb einen unscharfen Seiten-Screenshot statt des Logos. Neu erzeugt (180x180, vollflaechig ohne Alpha: iOS faerbt Transparenz schwarz, und icon-512.png hat nachgemessen transparente Ecken). Zentral ueber main.js in alle 34 Seiten eingehaengt statt 34x kopiert; gate.html hat es statisch, da ohne main.js. Zusaetzlich maskable-Icons ergaenzt: Android beschneidet das Startsymbol auf einen Kreis. Per Simulation der Sicherheitszone nachgemessen -- das bisherige Icon haette 19,87% des Logos verloren (Husky-Ohren + Schriftzug). Die neuen Varianten (60%/59% Fuellgrad) liegen bei exakt 0 abgeschnittenen Pixeln, Fuellgrad dafuer schrittweise eingemessen statt geraten. Markenfarbe #8FD9EA aus dem vorhandenen Icon ausgelesen, keine neue Farbe erfunden. Validiert: node --check, Manifest-JSON, Tag-Balance, alle 8 Pflicht-Zutaten vorhanden. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -6,6 +6,26 @@
|
||||
<title>Zugang — DOGFATHER UNIVERSE</title>
|
||||
<meta name="robots" content="noindex, nofollow" />
|
||||
<link rel="icon" type="image/png" href="assets/img/favicon.png" />
|
||||
<!-- BUGFIX (19.08.2026, Nutzer-Report vom Handy: "hab es installiert und klappt nicht"):
|
||||
Diese Seite ist der ERSTE Bildschirm, den man ueberhaupt zu sehen bekommt (ohne gueltige
|
||||
Sitzung leitet gate.js JEDEN Aufruf hierher um) -- also auch der Bildschirm, von dem aus
|
||||
man die App installiert. Sie hatte aber weder Manifest-Verweis noch Service-Worker-
|
||||
Registrierung, und beides verlangt Chrome, damit ueberhaupt eine echte, installierbare
|
||||
App entsteht. Ergebnis: Wer vor dem ersten Login installiert hat, bekam bestenfalls eine
|
||||
nackte Verknuepfung ohne Icon/Standalone-Modus. Exakt derselbe Fehler wie am 07.08.2026
|
||||
in VanVans Shop (dort gefixt) -- hier war er nur nie aufgefallen, weil die Seite hinter
|
||||
der Schranke (index.html) das alles laengst hatte. -->
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
<meta name="theme-color" content="#0b0d10" />
|
||||
<!-- iOS ignoriert das Manifest fuer das Startbildschirm-Symbol komplett und nimmt NUR
|
||||
apple-touch-icon. Fehlt es, zeigt iPhone/iPad einen unscharfen Screenshot der Seite
|
||||
statt des Logos. Bewusst eine eigene Datei OHNE Transparenz: iOS ersetzt Alpha durch
|
||||
Schwarz, und icon-512.png hat transparente Ecken (nachgemessen) -> haette schwarze
|
||||
Ecken gegeben. -->
|
||||
<link rel="apple-touch-icon" href="/assets/img/apple-touch-icon.png" />
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||
<meta name="apple-mobile-web-app-title" content="DogFather" />
|
||||
<link rel="stylesheet" href="assets/css/main.css" />
|
||||
<link rel="stylesheet" href="assets/css/theme-dogfather.css" />
|
||||
<style>
|
||||
@@ -281,35 +301,89 @@
|
||||
// Beide Kacheln (Dogi/VanVan) nutzen dieselbe Logik und denselben
|
||||
// /gate-auth-Endpunkt — nur das jeweils eigene Formular/Eingabefeld/
|
||||
// Fehlerfeld unterscheidet sich.
|
||||
/* BUGFIX (19.08.2026, Nutzer-Report vom Handy): Der Knopf gab beim Antippen KEINERLEI
|
||||
Rueckmeldung und die Anfrage hatte kein Zeitlimit. Bleibt im Mobilfunknetz eine Antwort
|
||||
aus, haengt fetch() unbegrenzt -- fuer die Person sieht es exakt so aus, als sei der Tap
|
||||
gar nicht angekommen ("er nimmt meinen Code nicht an"), obwohl in Wahrheit nur die
|
||||
Antwort fehlt. Zusaetzlich liess sich mehrfach hintereinander absenden, was bei der
|
||||
5-Versuche-Sperre in server/gate.js besonders unguenstig ist: Doppel-Taps zaehlen als
|
||||
mehrere Fehlversuche und koennen die eigene IP fuer 15 Minuten aussperren.
|
||||
Derselbe Fix wie am 06.08.2026 in VanVans gate.html/verwaltung-code.html. */
|
||||
const ZEITLIMIT_MS = 10000;
|
||||
|
||||
function bindeGateKachel(formId, inputId, fehlerId) {
|
||||
const form = document.getElementById(formId);
|
||||
const fehlerEl = document.getElementById(fehlerId);
|
||||
const btn = form.querySelector("button[type=submit]");
|
||||
const btnText = btn ? btn.querySelector(".btn-holo-text") : null;
|
||||
const originalText = btnText ? btnText.textContent : "";
|
||||
let laeuft = false;
|
||||
|
||||
function sperren(an) {
|
||||
laeuft = an;
|
||||
if (btn) btn.disabled = an;
|
||||
if (btnText) btnText.textContent = an ? "Wird geprüft…" : originalText;
|
||||
form.setAttribute("aria-busy", an ? "true" : "false");
|
||||
}
|
||||
|
||||
form.addEventListener("submit", async function (e) {
|
||||
e.preventDefault();
|
||||
if (laeuft) return; // Doppel-Tap ignorieren
|
||||
fehlerEl.classList.remove("zeige");
|
||||
const code = document.getElementById(inputId).value.trim();
|
||||
if (!code) return;
|
||||
if (!code) {
|
||||
fehlerEl.textContent = "Bitte zuerst den Zugangscode eingeben.";
|
||||
fehlerEl.classList.add("zeige");
|
||||
return;
|
||||
}
|
||||
|
||||
sperren(true);
|
||||
/* AbortController statt endlosem Warten. AbortSignal.timeout() waere kuerzer, kennen
|
||||
aeltere Handy-Browser aber nicht -- deshalb bewusst der breiter unterstuetzte Weg. */
|
||||
const abbruch = new AbortController();
|
||||
const timer = setTimeout(() => abbruch.abort(), ZEITLIMIT_MS);
|
||||
try {
|
||||
const res = await fetch("/gate-auth", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ code, next }),
|
||||
signal: abbruch.signal,
|
||||
});
|
||||
const data = await res.json();
|
||||
if (!res.ok || !data.ok) {
|
||||
const data = await res.json().catch(() => null);
|
||||
if (!res.ok || !data || !data.ok) {
|
||||
fehlerEl.textContent = (data && data.error) || "Falscher Zugangscode.";
|
||||
fehlerEl.classList.add("zeige");
|
||||
sperren(false);
|
||||
return;
|
||||
}
|
||||
/* Erfolg: Knopf bewusst gesperrt lassen — die Seite wird gleich gewechselt, ein
|
||||
erneuter Tap in der Zwischenzeit waere nur verwirrend. */
|
||||
if (btnText) btnText.textContent = "Willkommen 💙";
|
||||
location.href = data.next || "/index.html";
|
||||
} catch {
|
||||
fehlerEl.textContent = "Verbindung fehlgeschlagen. Bitte nochmal versuchen.";
|
||||
} catch (err) {
|
||||
fehlerEl.textContent =
|
||||
err && err.name === "AbortError"
|
||||
? "Zeitüberschreitung — Verbindung zu langsam. Bitte nochmal versuchen."
|
||||
: "Verbindung fehlgeschlagen. Bitte nochmal versuchen.";
|
||||
fehlerEl.classList.add("zeige");
|
||||
sperren(false);
|
||||
} finally {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
});
|
||||
}
|
||||
bindeGateKachel("gate-form-dogi", "gate-code-dogi", "gate-fehler-dogi");
|
||||
bindeGateKachel("gate-form-vanvan", "gate-code-vanvan", "gate-fehler-vanvan");
|
||||
|
||||
/* Service-Worker-Registrierung — auf allen anderen Seiten macht das main.js, diese Seite
|
||||
ist aber bewusst eigenstaendig und laedt main.js nicht. Ohne registrierten Service
|
||||
Worker bietet Chrome hier gar kein "App installieren" an (siehe Kommentar im Kopfbereich).
|
||||
Bewusst identisch defensiv wie in main.js: nur ueber http(s), Fehler still schlucken. */
|
||||
if ("serviceWorker" in navigator && /^https?:$/.test(location.protocol)) {
|
||||
window.addEventListener("load", () => {
|
||||
navigator.serviceWorker.register("/sw.js").catch(() => {});
|
||||
});
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
|
||||
Reference in New Issue
Block a user