Als App auf dem PC installierbar — mit gut sichtbarem Installations-Button
Die Icons/Manifeste waren schon vorbereitet, aber es gab keinen echten Weg, die Installation auch zu STARTEN, ohne das kleine, leicht zu übersehende Icon im Browser selbst zu suchen. Jetzt: - Ein Service Worker (public/sw.js) ist Voraussetzung dafür, dass Browser die Seite überhaupt als "installierbare App" erkennen. Bewusst minimal gehalten: strikt "network-first" — bei bestehender Verbindung wird IMMER zuerst das echte Netzwerk gefragt, der Cache dient nur als Rückfalllösung, wenn wirklich keine Verbindung besteht. Dadurch besteht KEIN Risiko, dass der Adminbereich veraltete Inhalte zeigt, während man online ist — als Bonus funktionieren bereits besuchte Seiten aber auch kurz offline (mit echtem Offline-Test geprüft). - Neuer "Installieren"-Button direkt in der Kopfzeile (Hauptseite) bzw. unten rechts (Adminbereich) — erscheint automatisch, sobald der Browser eine Installation wirklich anbietet, verschwindet danach wieder bzw. bleibt komplett versteckt, wenn schon als App installiert. Ein Klick reicht, kein Suchen im Browser-Menü mehr nötig. - Beide Bereiche (Hauptseite + Admin) nutzen dieselben, bereits vorhandenen Logo-Icons/Manifeste — landet also mit dem echten VanVan-Logo auf dem Startbildschirm/Desktop. Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
0ff6412e1e
commit
4aee6c7b98
@@ -71,6 +71,12 @@ const p = (href: string) => `${localePrefix(lang)}${href}`;
|
||||
</nav>
|
||||
|
||||
<div class="header-actions">
|
||||
<button type="button" id="install-btn" class="install-btn" style="display:none;" title={t.common.installApp}>
|
||||
<span class="install-icon" aria-hidden="true">
|
||||
<svg viewBox="0 0 24 24" width="14" height="14" fill="currentColor"><path d="M12 3a1 1 0 0 1 1 1v9.6l3-3a1 1 0 1 1 1.4 1.4l-4.7 4.7a1 1 0 0 1-1.4 0L6.6 12a1 1 0 1 1 1.4-1.4l3 3V4a1 1 0 0 1 1-1Zm-7 15a1 1 0 0 1 1-1h12a1 1 0 1 1 0 2H6a1 1 0 0 1-1-1Z"/></svg>
|
||||
</span>
|
||||
<span class="install-label">{t.common.installAppShort}</span>
|
||||
</button>
|
||||
<LanguageSwitcher lang={lang} path={path} legalOnly={legalOnly} />
|
||||
<a href={p("/konto/")} class="account-btn">
|
||||
<span class="account-icon" aria-hidden="true">
|
||||
@@ -166,6 +172,42 @@ const p = (href: string) => `${localePrefix(lang)}${href}`;
|
||||
const open = mobileNav?.classList.toggle("open");
|
||||
hamburger.setAttribute("aria-expanded", String(!!open));
|
||||
});
|
||||
|
||||
// "Auf dem Gerät installieren" — zeigt einen echten Button in der Kopfzeile, sobald der
|
||||
// Browser eine Installation anbietet (Chrome/Edge auf PC & Handy), statt sich darauf zu
|
||||
// verlassen, dass jemand das winzige Icon im Browser selbst findet. Läuft bereits als
|
||||
// installierte App? Dann gibt es sowieso nichts mehr zu installieren — Button bleibt versteckt.
|
||||
const installBtn = document.getElementById("install-btn");
|
||||
const laeuftBereitsAlsApp = window.matchMedia("(display-mode: standalone)").matches;
|
||||
let installPromptEvent = null;
|
||||
if (!laeuftBereitsAlsApp) {
|
||||
window.addEventListener("beforeinstallprompt", (e) => {
|
||||
e.preventDefault();
|
||||
installPromptEvent = e;
|
||||
if (installBtn) installBtn.style.display = "flex";
|
||||
});
|
||||
}
|
||||
installBtn?.addEventListener("click", async () => {
|
||||
if (!installPromptEvent) return;
|
||||
installBtn.style.display = "none";
|
||||
installPromptEvent.prompt();
|
||||
await installPromptEvent.userChoice;
|
||||
installPromptEvent = null;
|
||||
});
|
||||
window.addEventListener("appinstalled", () => {
|
||||
if (installBtn) installBtn.style.display = "none";
|
||||
});
|
||||
|
||||
// Service Worker: rein "network-first" (versucht IMMER zuerst das echte Netzwerk, Cache
|
||||
// dient nur als Rückfalllösung, wenn wirklich keine Verbindung besteht) — kein Risiko auf
|
||||
// veraltete Inhalte im Adminbereich, macht die Seite aber gleichzeitig zu einer "echten" App
|
||||
// (Installations-Voraussetzung in den meisten Browsern) und funktioniert notfalls auch kurz
|
||||
// offline für bereits besuchte Seiten.
|
||||
if ("serviceWorker" in navigator) {
|
||||
window.addEventListener("load", () => {
|
||||
navigator.serviceWorker.register("/sw.js").catch(() => {});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style is:global>
|
||||
@@ -216,6 +258,32 @@ const p = (href: string) => `${localePrefix(lang)}${href}`;
|
||||
|
||||
.header-actions { display: flex; align-items: center; gap: 0.8rem; }
|
||||
.icon-link { font-size: 1.25rem; position: relative; display: inline-flex; align-items: center; gap: 0.3rem; }
|
||||
|
||||
/* "Auf dem Gerät installieren"-Button — nur sichtbar, wenn der Browser das wirklich anbietet
|
||||
(per JS über das beforeinstallprompt-Event ein-/ausgeblendet, siehe Script unten). Damit
|
||||
muss niemand erst das kleine, leicht zu übersehende Installations-Icon im Browser selbst
|
||||
suchen — der Button ist direkt in der Kopfzeile sichtbar. */
|
||||
.install-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.45em;
|
||||
padding: 0.5em 0.9em 0.5em 0.7em;
|
||||
border-radius: 999px;
|
||||
background: linear-gradient(135deg, rgba(155, 127, 214, 0.22), rgba(46, 27, 71, 0.4));
|
||||
border: 1px solid rgba(155, 127, 214, 0.4);
|
||||
color: var(--c-text);
|
||||
font-size: 0.88rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.2s var(--ease), transform 0.2s var(--ease), box-shadow 0.2s var(--ease);
|
||||
}
|
||||
.install-btn:hover {
|
||||
border-color: var(--c-purple-glow);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 8px 20px -8px rgba(155, 127, 214, 0.5);
|
||||
}
|
||||
.install-icon { display: flex; }
|
||||
@media (max-width: 640px) { .install-btn .install-label { display: none; } .install-btn { padding: 0.5em; } }
|
||||
.cart-count {
|
||||
background: var(--c-accent);
|
||||
color: #08181c;
|
||||
|
||||
Reference in New Issue
Block a user