Aller au contenu

PWA — Progressive Web App

Primatch est une Progressive Web App installable sur mobile et desktop, offrant une expérience proche d'une application native.


Vue d'ensemble

Élément Valeur
Plugin vite-plugin-pwa v1.x (Workbox)
Mode generateSW (service worker auto-généré)
Register type prompt (l'utilisateur choisit quand mettre à jour)
Fichier manifest dist/manifest.webmanifest
Service worker dist/sw.js

Manifest

Le manifest est configuré directement dans vite.config.ts via le plugin VitePWA :

manifest: {
  name: 'Primatch — Padel entre amis',
  short_name: 'Primatch',
  description: 'Trouvez des partenaires de padel et organisez vos parties.',
  theme_color: '#00C2C2',
  background_color: '#F7F8FC',
  display: 'standalone',
  orientation: 'portrait',
  scope: '/',
  start_url: '/',
  icons: [
    { src: '/icon-192.svg',  sizes: '192x192', type: 'image/svg+xml' },
    { src: '/icon-512.svg',  sizes: '512x512', type: 'image/svg+xml' },
    { src: '/icon-maskable-512.svg', sizes: '512x512', type: 'image/svg+xml', purpose: 'maskable' },
  ],
}

Icônes

Les icônes au format SVG sont dans frontend/public/ :

Fichier Taille Usage
favicon.svg 32×32 Onglet navigateur
apple-touch-icon.svg 180×180 iOS « Ajouter à l'écran d'accueil »
icon-192.svg 192×192 Manifest (Android)
icon-512.svg 512×512 Manifest / splash screen
icon-maskable-512.svg 512×512 Adaptive icon (Android, zone de sécurité)

Service Worker (Workbox)

Stratégie de pré-cache

Tous les assets statiques sont pré-cachés automatiquement au build :

workbox: {
  globPatterns: ['**/*.{js,css,html,svg,png,woff2}'],
}

Le build génère environ 15 entrées de pré-cache (~630 Ko).

Cache runtime

Les requêtes vers les Google Fonts sont cachées en runtime avec une stratégie CacheFirst :

runtimeCaching: [
  {
    urlPattern: /^https:\/\/fonts\.googleapis\.com\/.*/i,
    handler: 'CacheFirst',
    options: {
      cacheName: 'google-fonts-cache',
      expiration: { maxEntries: 10, maxAgeSeconds: 60 * 60 * 24 * 365 },
      cacheableResponse: { statuses: [0, 200] },
    },
  },
  {
    urlPattern: /^https:\/\/fonts\.gstatic\.com\/.*/i,
    handler: 'CacheFirst',
    options: {
      cacheName: 'gstatic-fonts-cache',
      expiration: { maxEntries: 10, maxAgeSeconds: 60 * 60 * 24 * 365 },
      cacheableResponse: { statuses: [0, 200] },
    },
  },
]

Requêtes API

Les appels API (/api/v1/…) ne sont pas cachés par le service worker. La gestion du cache API est déléguée à React Query côté client.


Mise à jour automatique

Le service worker est enregistré avec registerType: 'prompt' :

  1. Vérification horaire — un setInterval de 1 h appelle registration.update() pour détecter les nouvelles versions.
  2. Bannière de mise à jour — quand needRefresh devient true, un composant PWAUpdatePrompt s'affiche en bas de l'écran.
  3. Action utilisateur — le joueur peut cliquer sur « Mettre à jour » pour activer le nouveau service worker, ou « Plus tard » pour ignorer.
┌────────────────────────────────────────────┐
│ 🔄 Mise à jour disponible                 │
│ Une nouvelle version est prête.            │
│                     [ Plus tard ] [ MAJ ]  │
└────────────────────────────────────────────┘

Invite d'installation

L'application intercepte l'événement beforeinstallprompt du navigateur :

  1. L'événement est différé et stocké dans un state React.
  2. Une bannière invite l'utilisateur à installer l'application.
  3. Au clic sur « Installer », deferredPrompt.prompt() est appelé.
  4. La bannière disparaît après la décision de l'utilisateur.

Priorité

Si une mise à jour ET une invitation d'installation sont disponibles simultanément, la mise à jour est affichée en priorité.


Composant PWAUpdatePrompt

Le composant se trouve dans src/components/ui/PWAUpdatePrompt.tsx et est monté dans main.tsx.

Props : aucune (composant autonome).

Hooks utilisés :

  • useRegisterSW (de virtual:pwa-register/react)
  • useTranslation (i18next, namespace common, clé pwa.*)

Traductions (dans src/i18n/locales/{fr,en}/common.json) :

Clé FR EN
pwa.update_available Mise à jour disponible Update available
pwa.update_message Une nouvelle version est prête. A new version is ready.
pwa.update Mettre à jour Update
pwa.later Plus tard Later
pwa.install_title Installer Primatch Install Primatch
pwa.install_message Ajoutez l'application à votre écran d'accueil. Add the app to your home screen.
pwa.install Installer Install

Meta tags

Les balises suivantes sont ajoutées dans index.html pour une compatibilité maximale :

<meta name="theme-color" content="#00C2C2" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="default" />
<meta name="apple-mobile-web-app-title" content="Primatch" />
<meta name="description" content="Trouvez des partenaires de padel …" />
<link rel="icon" href="/favicon.svg" />
<link rel="apple-touch-icon" href="/apple-touch-icon.svg" />

Vérification

Build

make shell-frontend        # ou : docker compose exec frontend sh
npx vite build

La sortie doit contenir :

dist/manifest.webmanifest   0.57 kB
dist/sw.js                  …
dist/workbox-*.js           …
PWA v1.x.x – mode: generateSW – precache XX entries (…)

Audit Lighthouse

  1. Ouvrir l'application déployée dans Chrome.
  2. DevTools → onglet Lighthouse → cocher Progressive Web App.
  3. Vérifier que le score PWA est vert (≥ 90).

Debugging

  • Chrome DevTools → Application → Service Workers : vérifier l'état du SW (activé, en attente…).
  • Chrome DevTools → Application → Manifest : vérifier le manifest chargé.
  • Chrome DevTools → Application → Cache Storage : inspecter les caches Workbox.

Développement local

Le mode dev PWA est activé (devOptions: { enabled: true }) pour tester le service worker en développement. Le SW est régénéré à chaque changement.

Limitations en dev

Le cache et l'installation PWA ne fonctionnent pleinement qu'en HTTPS ou sur localhost. En mode dev, certaines fonctionnalités (notification push, installation) peuvent être limitées selon le navigateur.