From 7f1bdaa2abfad12afde51bf5b57508fbb69c8ac9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20FAMIBELLE-PRONZOLA?= Date: Fri, 24 Jul 2026 19:35:13 +0400 Subject: [PATCH] feat: add styled PWA update modal with auto cache purge --- README.adoc | 36 ++++++++-- categories.php | 1 + conf/.htaccess.sample | 12 ++++ conf/nginx.conf.sample | 12 ++++ countdown.php | 1 + css/styles.css | 116 +++++++++++++++++++++++++++++- direct.php | 1 + dons.php.sample | 1 + index.php | 31 ++------ js/pwa-update.js | 138 ++++++++++++++++++++++++++++++++++++ mentions-legales.php.sample | 1 + recherche.php | 1 + sw.js | 31 ++++---- video.php | 1 + 14 files changed, 331 insertions(+), 52 deletions(-) create mode 100644 js/pwa-update.js diff --git a/README.adoc b/README.adoc index a2b36c4..31ff619 100644 --- a/README.adoc +++ b/README.adoc @@ -192,7 +192,8 @@ Toutes ces sources sont modifiables dans `includes/config.local.php` (voir < + diff --git a/conf/.htaccess.sample b/conf/.htaccess.sample index 303524b..46b1bb9 100644 --- a/conf/.htaccess.sample +++ b/conf/.htaccess.sample @@ -55,3 +55,15 @@ RewriteRule ^([^/]+)$ $1.php [L] RewriteCond %{HTTP:X-Forwarded-Proto} !https RewriteCond %{HTTPS} !on RewriteRule ^(.*) https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301] + +# ====================== +# CACHE +# ====================== + +# Le Service Worker et le manifest ne doivent pas être cachés longtemps : +# le navigateur doit pouvoir détecter les nouvelles versions du site. + + + Header set Cache-Control "no-cache, must-revalidate" + + diff --git a/conf/nginx.conf.sample b/conf/nginx.conf.sample index 3cddbfd..878b100 100644 --- a/conf/nginx.conf.sample +++ b/conf/nginx.conf.sample @@ -75,6 +75,18 @@ server { # OPTIMISATIONS # ====================== + # Le Service Worker et le manifest ne doivent pas être cachés longtemps : + # le navigateur doit pouvoir détecter les nouvelles versions du site. + # (location exacte = prioritaire sur les regex ci-dessous ; "expires -1" + # émet Cache-Control: no-cache sans annuler les add_header de sécurité) + location = /sw.js { + expires -1; + } + + location = /site.webmanifest { + expires -1; + } + # Cache des fichiers statiques location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { expires 1y; diff --git a/countdown.php b/countdown.php index aae4781..112c455 100644 --- a/countdown.php +++ b/countdown.php @@ -183,5 +183,6 @@ setSecurityHeaders(); + \ No newline at end of file diff --git a/css/styles.css b/css/styles.css index 52209ab..e5eed1f 100644 --- a/css/styles.css +++ b/css/styles.css @@ -3411,4 +3411,118 @@ i.icon-mastodon, } } - \ No newline at end of file + +/* ========================================== + Modal de mise à jour PWA (js/pwa-update.js) + ========================================== */ + +.pwa-update-overlay { + position: fixed; + inset: 0; + background-color: rgba(0, 0, 0, 0.55); + display: flex; + align-items: center; + justify-content: center; + padding: 20px; + z-index: 10000; + opacity: 0; + transition: opacity 0.25s ease; +} + +.pwa-update-overlay.pwa-update-visible { + opacity: 1; +} + +.pwa-update-modal { + background-color: var(--card-bg); + color: var(--text-color); + border: 1px solid var(--border-color); + border-radius: 12px; + box-shadow: 0 10px 40px rgba(0, 0, 0, 0.35); + max-width: 420px; + width: 100%; + padding: 28px 24px 24px; + text-align: center; + transform: translateY(12px) scale(0.97); + transition: transform 0.25s ease; +} + +.pwa-update-visible .pwa-update-modal { + transform: translateY(0) scale(1); +} + +.pwa-update-logo { + width: 72px; + height: 72px; + border-radius: 50%; + margin-bottom: 12px; +} + +.pwa-update-title { + margin: 0 0 8px; + font-size: 1.25rem; + font-weight: 700; +} + +.pwa-update-text { + margin: 0 0 20px; + color: var(--text-secondary); + font-size: 0.95rem; + line-height: 1.5; +} + +.pwa-update-actions { + display: flex; + gap: 12px; + justify-content: center; + flex-wrap: wrap; +} + +.pwa-update-btn { + border: none; + border-radius: 8px; + padding: 10px 20px; + font-size: 0.95rem; + font-weight: 600; + cursor: pointer; + transition: background-color 0.2s ease, opacity 0.2s ease; +} + +.pwa-update-btn:disabled { + opacity: 0.6; + cursor: wait; +} + +.pwa-update-btn-primary { + background-color: var(--primary-red); + color: #ffffff; +} + +.pwa-update-btn-primary:hover:not(:disabled) { + filter: brightness(0.9); +} + +.pwa-update-btn-secondary { + background-color: transparent; + color: var(--text-color); + border: 1px solid var(--border-color); +} + +.pwa-update-btn-secondary:hover:not(:disabled) { + background-color: var(--hover-bg); +} + +.pwa-update-btn:focus-visible { + outline: 2px solid var(--primary-red); + outline-offset: 2px; +} + +@media (max-width: 480px) { + .pwa-update-actions { + flex-direction: column; + } + + .pwa-update-btn { + width: 100%; + } +} diff --git a/direct.php b/direct.php index 86b20bd..ee8bb68 100644 --- a/direct.php +++ b/direct.php @@ -274,5 +274,6 @@ $liveStream = getLiveStream(); + \ No newline at end of file diff --git a/dons.php.sample b/dons.php.sample index 2a49658..4a09025 100644 --- a/dons.php.sample +++ b/dons.php.sample @@ -291,5 +291,6 @@ $stripeMonthlyLinks = defined('STRIPE_MONTHLY_LINKS') ? STRIPE_MONTHLY_LINKS : [ + diff --git a/index.php b/index.php index f37e4ec..2a3cfea 100644 --- a/index.php +++ b/index.php @@ -678,34 +678,11 @@ setSecurityHeaders(); - + + + + + diff --git a/recherche.php b/recherche.php index 8670d4d..cc0efda 100644 --- a/recherche.php +++ b/recherche.php @@ -261,5 +261,6 @@ if ($resultsCount > 0) { + diff --git a/sw.js b/sw.js index de0a5e9..9c71b8c 100644 --- a/sw.js +++ b/sw.js @@ -1,6 +1,9 @@ -const CACHE_NAME = 'fediverse-oki-08072026-0720'; -const STATIC_CACHE_NAME = 'fediverse-oki-static-08072026-0720'; -const DYNAMIC_CACHE_NAME = 'fediverse-oki-dynamic-08072026-0720'; +// Version du cache : à bumper à chaque déploiement (format JJMMAAAA-HHMM). +// Tout changement de ce fichier déclenche l'installation d'un nouveau +// Service Worker chez les visiteurs ; les anciens caches sont purgés à +// l'activation (voir l'event 'activate' plus bas). +const STATIC_CACHE_NAME = 'annu-kute-ced-static-24072026-0612'; +const DYNAMIC_CACHE_NAME = 'annu-kute-ced-dynamic-24072026-0612'; // Ressources à mettre en cache immédiatement const STATIC_ASSETS = [ @@ -46,13 +49,15 @@ self.addEventListener('install', event => { console.log('Service Worker: Mise en cache des assets statiques'); return cache.addAll(STATIC_ASSETS); }) - .then(() => { - return self.skipWaiting(); - }) .catch(err => { console.error('Service Worker: Erreur lors de la mise en cache:', err); }) ); + // Note : pas de skipWaiting() ici. Lors d'une mise à jour, le nouveau SW + // reste en attente jusqu'à ce que l'utilisateur accepte la mise à jour + // via le modal (message SKIP_WAITING envoyé par js/pwa-update.js). + // Lors de la toute première visite (aucun SW actif), l'activation est + // immédiate. }); // Activation du Service Worker @@ -202,20 +207,10 @@ function isApiRequest(url) { url.includes('mastodon-config.php'); } -// Gestion des messages du client +// Gestion des messages du client : l'utilisateur a accepté la mise à jour, +// le SW en attente prend le contrôle (purge des anciens caches à l'activation). self.addEventListener('message', event => { if (event.data && event.data.type === 'SKIP_WAITING') { self.skipWaiting(); } }); - -// Notification de mise à jour -self.addEventListener('message', event => { - if (event.data && event.data.type === 'CHECK_UPDATE') { - // Vérifier s'il y a une mise à jour - event.ports[0].postMessage({ - type: 'UPDATE_AVAILABLE', - version: CACHE_NAME - }); - } -}); diff --git a/video.php b/video.php index 400af99..eee3f13 100644 --- a/video.php +++ b/video.php @@ -668,5 +668,6 @@ if (empty($videoData) || isset($videoData['error'])) { }); + \ No newline at end of file