Initial commit: KGV-PWA plugin with manifest, service worker and install prompt
This commit is contained in:
124
assets/js/pwa-register.js
Normal file
124
assets/js/pwa-register.js
Normal file
@@ -0,0 +1,124 @@
|
||||
/**
|
||||
* KGV PWA – Service Worker Registrierung & Install-Prompt
|
||||
*/
|
||||
|
||||
( function () {
|
||||
'use strict';
|
||||
|
||||
var cfg = window.kgvPwaConfig || {};
|
||||
var swUrl = cfg.swUrl || '/sw.js';
|
||||
var scope = cfg.scope || '/';
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Service Worker registrieren
|
||||
// ------------------------------------------------------------------
|
||||
if ( 'serviceWorker' in navigator ) {
|
||||
window.addEventListener( 'load', function () {
|
||||
navigator.serviceWorker
|
||||
.register( swUrl, { scope: scope } )
|
||||
.catch( function ( err ) {
|
||||
if ( window.console && console.warn ) {
|
||||
console.warn( 'KGV PWA: Service Worker konnte nicht registriert werden.', err );
|
||||
}
|
||||
} );
|
||||
} );
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Install-Banner
|
||||
// ------------------------------------------------------------------
|
||||
var deferredPrompt = null;
|
||||
var banner = null;
|
||||
|
||||
/**
|
||||
* Banner-Element erzeugen und in den DOM einhängen.
|
||||
*/
|
||||
function createBanner() {
|
||||
banner = document.createElement( 'div' );
|
||||
banner.id = 'kgv-pwa-install-banner';
|
||||
banner.setAttribute( 'role', 'region' );
|
||||
banner.setAttribute( 'aria-label', 'App installieren' );
|
||||
banner.innerHTML =
|
||||
'<span class="kgv-pwa-banner__text">Diese Seite als App installieren</span>' +
|
||||
'<button class="kgv-pwa-banner__install" type="button">Installieren</button>' +
|
||||
'<button class="kgv-pwa-banner__close" type="button" aria-label="Schließen">×</button>';
|
||||
|
||||
document.body.appendChild( banner );
|
||||
|
||||
banner.querySelector( '.kgv-pwa-banner__install' ).addEventListener( 'click', triggerInstall );
|
||||
banner.querySelector( '.kgv-pwa-banner__close' ).addEventListener( 'click', dismissBanner );
|
||||
}
|
||||
|
||||
function showBanner() {
|
||||
if ( ! banner ) {
|
||||
createBanner();
|
||||
}
|
||||
banner.classList.add( 'kgv-pwa-banner--visible' );
|
||||
}
|
||||
|
||||
function dismissBanner() {
|
||||
if ( banner ) {
|
||||
banner.classList.remove( 'kgv-pwa-banner--visible' );
|
||||
}
|
||||
// Nicht mehr anzeigen bis zum nächsten Tag
|
||||
try {
|
||||
sessionStorage.setItem( 'kgv_pwa_install_dismissed', '1' );
|
||||
} catch ( e ) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Nativen Browser-Installationsdialog öffnen.
|
||||
* Kann auch von einem beliebigen Button auf der Seite aufgerufen werden:
|
||||
* document.dispatchEvent( new CustomEvent('kgv-pwa-install') );
|
||||
*/
|
||||
function triggerInstall() {
|
||||
if ( ! deferredPrompt ) {
|
||||
if ( isIos() ) {
|
||||
window.alert( cfg.iosInstallNotice || 'Auf iPhone/iPad: Teilen > Zum Home-Bildschirm.' );
|
||||
}
|
||||
return;
|
||||
}
|
||||
deferredPrompt.prompt();
|
||||
deferredPrompt.userChoice.then( function ( result ) {
|
||||
deferredPrompt = null;
|
||||
dismissBanner();
|
||||
if ( window.console && console.info ) {
|
||||
console.info( 'KGV PWA: Installationsentscheidung:', result.outcome );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
// Externer Aufruf via Custom Event ermöglichen
|
||||
document.addEventListener( 'kgv-pwa-install', triggerInstall );
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// beforeinstallprompt – Browser signalisiert Installierbarkeit
|
||||
// ------------------------------------------------------------------
|
||||
window.addEventListener( 'beforeinstallprompt', function ( e ) {
|
||||
// Standard-Mini-Infobar des Browsers unterdrücken
|
||||
e.preventDefault();
|
||||
deferredPrompt = e;
|
||||
|
||||
// Banner nicht zeigen, wenn der Nutzer ihn in dieser Sitzung bereits weggeklickt hat
|
||||
try {
|
||||
if ( sessionStorage.getItem( 'kgv_pwa_install_dismissed' ) ) {
|
||||
return;
|
||||
}
|
||||
} catch ( err ) {}
|
||||
|
||||
showBanner();
|
||||
} );
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// appinstalled – nach erfolgreicher Installation Banner verbergen
|
||||
// ------------------------------------------------------------------
|
||||
window.addEventListener( 'appinstalled', function () {
|
||||
deferredPrompt = null;
|
||||
dismissBanner();
|
||||
} );
|
||||
|
||||
function isIos() {
|
||||
return /iphone|ipad|ipod/i.test( window.navigator.userAgent || '' );
|
||||
}
|
||||
|
||||
} )();
|
||||
117
assets/js/service-worker.js
Normal file
117
assets/js/service-worker.js
Normal file
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
* KGV PWA – Service Worker
|
||||
*
|
||||
* Strategie:
|
||||
* - HTML-Seiten: Network-first (frische Inhalte, Fallback auf Cache)
|
||||
* - Statische Assets (CSS/JS/Bilder/Fonts): Cache-first
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const CACHE_NAME = 'kgv-pwa-v1';
|
||||
const OFFLINE_PAGE = '/';
|
||||
|
||||
const PRECACHE_URLS = [
|
||||
'/',
|
||||
];
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Install: Precache-Ressourcen laden
|
||||
// -----------------------------------------------------------------------
|
||||
self.addEventListener( 'install', function ( event ) {
|
||||
event.waitUntil(
|
||||
caches.open( CACHE_NAME ).then( function ( cache ) {
|
||||
return cache.addAll( PRECACHE_URLS );
|
||||
} )
|
||||
);
|
||||
self.skipWaiting();
|
||||
} );
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Activate: Alte Caches löschen
|
||||
// -----------------------------------------------------------------------
|
||||
self.addEventListener( 'activate', function ( event ) {
|
||||
event.waitUntil(
|
||||
caches.keys().then( function ( keys ) {
|
||||
return Promise.all(
|
||||
keys
|
||||
.filter( function ( key ) { return key !== CACHE_NAME; } )
|
||||
.map( function ( key ) { return caches.delete( key ); } )
|
||||
);
|
||||
} )
|
||||
);
|
||||
self.clients.claim();
|
||||
} );
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Fetch: Anfragen abfangen
|
||||
// -----------------------------------------------------------------------
|
||||
self.addEventListener( 'fetch', function ( event ) {
|
||||
var request = event.request;
|
||||
|
||||
// Nur GET-Anfragen behandeln
|
||||
if ( request.method !== 'GET' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Nur Anfragen gleichen Ursprungs behandeln
|
||||
if ( ! request.url.startsWith( self.location.origin ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var url = new URL( request.url );
|
||||
|
||||
// Admin-Bereich und WP-Cron ausschließen
|
||||
if (
|
||||
url.pathname.startsWith( '/wp-admin' ) ||
|
||||
url.pathname.startsWith( '/wp-cron' ) ||
|
||||
url.pathname.includes( 'wp-login' )
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// HTML-Navigation: Network-first
|
||||
if ( request.mode === 'navigate' ) {
|
||||
event.respondWith(
|
||||
fetch( request )
|
||||
.then( function ( response ) {
|
||||
// Erfolgreiche Antwort im Cache speichern
|
||||
if ( response && response.status === 200 ) {
|
||||
var clone = response.clone();
|
||||
caches.open( CACHE_NAME ).then( function ( cache ) {
|
||||
cache.put( request, clone );
|
||||
} );
|
||||
}
|
||||
return response;
|
||||
} )
|
||||
.catch( function () {
|
||||
// Offline-Fallback: gecachte Startseite
|
||||
return caches.match( request ).then( function ( cached ) {
|
||||
return cached || caches.match( OFFLINE_PAGE );
|
||||
} );
|
||||
} )
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Statische Assets: Cache-first
|
||||
if ( /\.(css|js|png|jpg|jpeg|gif|svg|webp|woff2?|ttf|eot|ico)(\?.*)?$/.test( url.pathname ) ) {
|
||||
event.respondWith(
|
||||
caches.match( request ).then( function ( cached ) {
|
||||
if ( cached ) {
|
||||
return cached;
|
||||
}
|
||||
return fetch( request ).then( function ( response ) {
|
||||
if ( response && response.status === 200 ) {
|
||||
var clone = response.clone();
|
||||
caches.open( CACHE_NAME ).then( function ( cache ) {
|
||||
cache.put( request, clone );
|
||||
} );
|
||||
}
|
||||
return response;
|
||||
} );
|
||||
} )
|
||||
);
|
||||
return;
|
||||
}
|
||||
} );
|
||||
Reference in New Issue
Block a user