Die Seite laesst sich als App installieren
Das Manifest war lange vollstaendig -- die App LIESS sich installieren. Nur bot das ausschliesslich der Browser an, versteckt in seinem Dreipunktemenue. Wer es nicht sucht, findet es nie. Und ohne installierte App gibt es auf dem Handy keine verlaesslichen Benachrichtigungen; genau daran hing im September das Telefonieren. Jetzt steht der Knopf in der Kopfleiste, auf allen Seiten, mit drei Antworten statt einer: schon installiert -> gar kein Knopf Browser bietet an -> der Knopf fragt ihn Safari am iPhone -> der Knopf erklaert den Weg Der dritte Fall ist der gefaehrliche: Dort gibt es beforeinstallprompt nicht und wird es nicht geben. Ein Knopf, der am iPhone nichts tut, waere schlimmer als keiner -- man drueckt ihn und sucht den Fehler bei sich. pruef-installieren.mjs: 14 Pruefungen, alle drei Zustaende, mit Gegenprobe in der installierten App. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -1022,3 +1022,43 @@
|
||||
.tperson[data-rolle="hand"] { border: 3px solid Highlight; }
|
||||
.tperson[data-rolle="hand"]::before { display: none; }
|
||||
}
|
||||
|
||||
/* ---- Der Installieren-Knopf (20.09.2026) ---------------------------
|
||||
|
||||
Er sieht aus wie die anderen Knöpfe der Kopfleiste -- mit einem
|
||||
Unterschied: ein leiser Akzentrand. Er ist ein Angebot, keine
|
||||
Warnung, und er verschwindet, sobald er überflüssig ist. Deshalb
|
||||
darf er auffallen, ohne zu drängen. */
|
||||
.install-knopf {
|
||||
appearance: none;
|
||||
display: inline-flex; align-items: center; gap: 7px;
|
||||
min-height: 44px; padding: 0 13px;
|
||||
border-radius: var(--radius-klein);
|
||||
border: 1px solid color-mix(in srgb, var(--akzent) 45%, transparent);
|
||||
background: color-mix(in srgb, var(--akzent) 12%, transparent);
|
||||
color: var(--text);
|
||||
font: inherit; font-size: .86rem; font-weight: 600;
|
||||
cursor: pointer; white-space: nowrap;
|
||||
transition: background var(--tempo), border-color var(--tempo);
|
||||
}
|
||||
.install-knopf:hover {
|
||||
background: color-mix(in srgb, var(--akzent) 22%, transparent);
|
||||
border-color: color-mix(in srgb, var(--akzent) 70%, transparent);
|
||||
}
|
||||
.install-knopf:focus-visible { outline: 2px solid var(--akzent); outline-offset: 2px; }
|
||||
.install-knopf__zeichen {
|
||||
flex: none; width: 17px; height: 17px;
|
||||
fill: none; stroke: currentColor; stroke-width: 1.8;
|
||||
stroke-linecap: round; stroke-linejoin: round;
|
||||
}
|
||||
/* AM HANDY NUR DAS ZEICHEN. Die Leiste ist dort eng, und ein Knopf mit
|
||||
Pfeil-nach-unten-in-eine-Ablage wird als „herunterladen/installieren"
|
||||
verstanden -- das Wort kostet 90 px, die woanders fehlen. Der
|
||||
`aria-label` sagt es weiterhin vollständig. */
|
||||
@media (max-width: 760px) {
|
||||
.install-knopf__text { display: none; }
|
||||
.install-knopf { padding: 0; width: 44px; justify-content: center; }
|
||||
}
|
||||
@media (forced-colors: active) {
|
||||
.install-knopf { border: 1px solid CanvasText; }
|
||||
}
|
||||
|
||||
@@ -1032,6 +1032,138 @@
|
||||
const kopfRechts = document.querySelector('.kopfleiste__rechts');
|
||||
if (kopfRechts) aufbauSuche(kopfRechts);
|
||||
if (kopfRechts) aufbauChat(kopfRechts);
|
||||
if (kopfRechts) aufbauInstallieren(kopfRechts);
|
||||
|
||||
/* =====================================================================
|
||||
DIE SEITE ALS APP INSTALLIEREN (20.09.2026)
|
||||
|
||||
Filipe: „kannst du da dazwischen in der starseite oben, ein button
|
||||
reinsetzen damit die leute die seite installieren koennen auf dem
|
||||
handy oder pc. das soll auch verschwinden wenn die leute es
|
||||
installiert haben."
|
||||
|
||||
WARUM ES DEN KNOPF BRAUCHT: Das Manifest ist seit Langem
|
||||
vollstaendig -- die App LIESS sich installieren. Nur bot das
|
||||
ausschliesslich der Browser selbst an, versteckt in seinem
|
||||
Dreipunktemenue. Wer es nicht sucht, findet es nie. Und ohne
|
||||
installierte App gibt es auf dem Handy keine verlaesslichen
|
||||
Benachrichtigungen; genau daran hing im September das Telefonieren.
|
||||
|
||||
DREI ZUSTAENDE, UND JEDER BRAUCHT EINE ANDERE ANTWORT:
|
||||
|
||||
1. SCHON INSTALLIERT -> gar kein Knopf. Ein Angebot fuer etwas,
|
||||
das man hat, ist Rauschen. Erkannt an `display-mode:
|
||||
standalone` (und an Safaris eigenem `navigator.standalone`).
|
||||
|
||||
2. DER BROWSER BIETET ES AN (Chrome, Edge, Android) -> der Knopf
|
||||
fragt ihn. `beforeinstallprompt` kommt EINMAL und muss
|
||||
aufgehoben werden; wer ihn nicht festhaelt, kann spaeter
|
||||
nicht mehr fragen.
|
||||
|
||||
3. SAFARI AUF DEM iPHONE -> dort gibt es `beforeinstallprompt`
|
||||
NICHT, und es wird ihn nicht geben. Apple laesst nur den Weg
|
||||
ueber „Teilen -> Zum Home-Bildschirm". Ein Knopf, der dort
|
||||
nichts tut, waere schlimmer als keiner -- also sagt er in
|
||||
diesem Fall, was zu tun ist, statt es zu versuchen.
|
||||
|
||||
WARUM IN DER KOPFLEISTE UND NICHT NUR AUF DER STARTSEITE: Die
|
||||
Leiste steht auf allen 32 Seiten. Wer den Knopf einmal uebersieht,
|
||||
sieht ihn beim naechsten Mal wieder -- und er verschwindet ohnehin,
|
||||
sobald er ueberfluessig ist.
|
||||
===================================================================== */
|
||||
|
||||
function aufbauInstallieren(wohin) {
|
||||
const el = (tag, klasse, text) => {
|
||||
const k = document.createElement(tag);
|
||||
if (klasse) k.className = klasse;
|
||||
if (text !== undefined) k.textContent = text;
|
||||
return k;
|
||||
};
|
||||
|
||||
/* ---- Zustand 1: schon installiert -> nichts tun --------------- */
|
||||
const schonDrin = () =>
|
||||
(window.matchMedia && window.matchMedia('(display-mode: standalone)').matches)
|
||||
|| window.navigator.standalone === true;
|
||||
if (schonDrin()) return;
|
||||
|
||||
/* Apple: kein `beforeinstallprompt`, aber ein Weg. */
|
||||
const istApple = /iphone|ipad|ipod/i.test(navigator.userAgent)
|
||||
|| (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1);
|
||||
|
||||
let angebot = null; // der aufgehobene Browser-Vorschlag
|
||||
|
||||
const knopf = el('button', 'install-knopf');
|
||||
knopf.type = 'button';
|
||||
knopf.hidden = true;
|
||||
knopf.title = 'Diese Seite als App installieren';
|
||||
/* DAS WORT VERSCHWINDET AM HANDY (siehe CSS) -- der Satz fuer
|
||||
Vorleseprogramme nicht. Ein Knopf, der dort nur aus einem
|
||||
Pfeil besteht, heisst sonst "Schaltflaeche". */
|
||||
knopf.setAttribute('aria-label', 'Diese Seite als App installieren');
|
||||
const zeichen = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
||||
zeichen.setAttribute('viewBox', '0 0 24 24');
|
||||
zeichen.setAttribute('class', 'install-knopf__zeichen');
|
||||
zeichen.setAttribute('aria-hidden', 'true');
|
||||
zeichen.innerHTML = '<path d="M12 3.5v11M8 11l4 4 4-4"/><path d="M5 18.5h14"/>';
|
||||
knopf.append(zeichen, el('span', 'install-knopf__text', 'Installieren'));
|
||||
|
||||
knopf.addEventListener('click', async () => {
|
||||
if (angebot) {
|
||||
/* Zustand 2: den Browser fragen. Die Antwort interessiert uns
|
||||
nur insofern, als ein angenommener Vorschlag den Knopf
|
||||
ueberfluessig macht -- ein abgelehnter nicht: Vielleicht
|
||||
ueberlegt man es sich morgen anders. */
|
||||
knopf.disabled = true;
|
||||
try {
|
||||
angebot.prompt();
|
||||
const { outcome } = await angebot.userChoice;
|
||||
if (outcome === 'accepted') knopf.remove();
|
||||
} catch { /* dann eben nicht */ }
|
||||
angebot = null;
|
||||
knopf.disabled = false;
|
||||
return;
|
||||
}
|
||||
/* Zustand 3: erklaeren statt versuchen. */
|
||||
window.frageNach?.({
|
||||
titel: 'Als App auf dem Home-Bildschirm',
|
||||
was: istApple
|
||||
? 'Tippe unten in Safari auf das Teilen-Zeichen (Viereck mit Pfeil nach oben), '
|
||||
+ 'dann auf „Zum Home-Bildschirm".'
|
||||
: 'Öffne das Menü deines Browsers (die drei Punkte) und wähle dort '
|
||||
+ '„App installieren" oder „Zum Startbildschirm hinzufügen".',
|
||||
bleibt: 'Danach liegt die Seite wie eine App auf deinem Gerät – und '
|
||||
+ 'Anrufe und Nachrichten kommen zuverlässiger an.',
|
||||
ja: 'Alles klar',
|
||||
nein: null,
|
||||
});
|
||||
});
|
||||
|
||||
wohin.prepend(knopf);
|
||||
|
||||
/* Der Browser meldet sich, wenn er es anbieten kann. */
|
||||
window.addEventListener('beforeinstallprompt', (e) => {
|
||||
/* Ohne `preventDefault` zeigt der Browser seine eigene Leiste --
|
||||
zwei Angebote fuer dieselbe Sache sind eines zu viel. */
|
||||
e.preventDefault();
|
||||
angebot = e;
|
||||
knopf.hidden = false;
|
||||
});
|
||||
|
||||
/* Auf Apple gibt es diese Meldung nie -- dort steht der Knopf von
|
||||
Anfang an da und erklaert den Weg. */
|
||||
if (istApple) knopf.hidden = false;
|
||||
|
||||
/* Und wenn es geklappt hat, ist er fertig. */
|
||||
window.addEventListener('appinstalled', () => knopf.remove());
|
||||
/* Manche Browser melden `appinstalled` nicht -- dann faellt der
|
||||
Knopf beim naechsten Oeffnen ueber `schonDrin()` weg. Und wer die
|
||||
App waehrend der Sitzung installiert und zurueckwechselt, merkt
|
||||
es hier: */
|
||||
try {
|
||||
window.matchMedia('(display-mode: standalone)')
|
||||
.addEventListener('change', (m) => { if (m.matches) knopf.remove(); });
|
||||
} catch { /* aeltere Browser koennen das nicht -- kein Grund zu scheitern */ }
|
||||
}
|
||||
|
||||
/* ---------- Der Chat-Knopf mit Zähler -----------------------------------
|
||||
|
||||
|
||||
Reference in New Issue
Block a user