Ir al contenido

PWA y push notifications

CÉNIT es una PWA instalable: el staff y los jugadores pueden agregarla a la pantalla de inicio en iOS, Android o Chrome desktop. El service worker maneja navegación offline básica y entrega push notifications del staff al jugador (mensajería interna, alertas). Toda la infraestructura es propia — no usa FCM ni APNs directos, sino Web Push estándar con claves VAPID.

  • iOS: el jugador (o staff) abre cenitams.com en Safari, “Compartir → Añadir a pantalla de inicio”.
  • Android / Chrome desktop: aparece un prompt nativo de instalación. El hook usePwaInstall (components/pwa/use-pwa-install.ts) detecta el evento beforeinstallprompt y expone un botón.
  • Una vez instalada, abre en modo standalone (sin barra de navegador), con start URL /dashboard para staff (configurable en app/manifest.ts).
  • Suscripción: el jugador, desde /player, toca “Activar notificaciones”. El componente push-subscribe-button.tsx solicita permiso, registra el endpoint y lo guarda en la tabla push_subscriptions vía POST /api/push/subscribe.
  • Envío: cada vez que el staff manda un mensaje al jugador desde staff-message-thread.tsx, sendStaffMessage dispara POST /api/push/send. La librería web-push firma el payload con VAPID y lo entrega al endpoint del navegador del jugador.
  • El staff no decide manualmente cuándo notificar — es automático al enviar el mensaje. No hay broadcast masivo.
  • Variables de entorno requeridas en Workers Secrets: NEXT_PUBLIC_VAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY, VAPID_SUBJECT.
  • Si faltan, la suscripción y el envío fallan silenciosamente — revisar logs en Cloudflare Workers.

El jugador instala la app desde su navegador. Después de habilitar notificaciones, cada mensaje del staff llega como notificación nativa del sistema operativo, con vibración corta ([100, 50, 100]) y el ícono de CÉNIT. Tocar la notificación abre /player/mensajes directamente — si la app ya está abierta, simplemente trae la ventana al frente.

  • push_subscriptions (endpoint, p256dh, auth_key, user_id) — una entrada por dispositivo del jugador. No hay TTL — endpoints inválidos quedan hasta que web-push retorne 410 y el handler los borre.

public/sw.js registra tres estrategias:

  • Cache-first para assets inmutables (/_next/static/, imágenes, fonts). Sirve sin red si ya están en caché.
  • Stale-while-revalidate para navegaciones (request.mode === 'navigate'): sirve la versión cacheada y refresca en background. Si no hay caché y la red falla, redirige a /offline.
  • Network-first para todo lo demás (data fetches): si la red falla, intenta caché; si tampoco, 503.

Rutas autenticadas (/dashboard, /player, /superadmin, /admin, /api/) están en NO_CACHE_PATHS — siempre network-only, nunca cachean respuestas con datos sensibles.

  • Mensajería interna (/player/mensajes, staff-message-thread.tsx) — dispara push automáticamente.
  • Cloudflare Workers — runtime para web-push. El SW se sirve desde /public/sw.js con scope /.
  • No hay broadcast a todo el plantel desde la UI — un mensaje, una notificación.
  • iOS Safari soporta Web Push desde iOS 16.4, pero requiere que la PWA esté instalada en pantalla de inicio. En navegador normal no recibe push.
  • Background sync (sync event con tag send-messages) está registrado en el SW pero los clients flushean su propia cola IDB — no hay queue persistente del lado del SW.
  • No hay UI para que el jugador desuscriba un dispositivo específico — desactivar requiere bloquear notificaciones en el navegador.