Sistema de Actualización PWA Implementado (v1.0.87)

Frontend (v1.0.87)
Service Worker (public/service-worker.js)
 Cache versionado dinámico: ayutec-v1.0.87
 Estrategia Network-First con fallback a cache
 Auto-limpieza de caches antiguos en activación
 Skip waiting para activación inmediata
 Soporte para mensaje SKIP_WAITING desde cliente
Detección de Actualizaciones (App.jsx)
 Registro automático de Service Worker
 Listener de updatefound para detectar nuevas versiones
 Listener de controllerchange para recarga automática
 Estado updateAvailable y waitingWorker
Modal de Actualización
 Diseño grande y llamativo con animación bounce
 Overlay bloqueante (z-index 9999, no se puede cerrar)
 Botón enorme: "🚀 ACTUALIZAR AHORA"
 Gradiente indigo/purple, responsive
 Texto claro: "Nueva versión disponible"
 Recarga automática al actualizar
PWA Manifest (site.webmanifest)
 Agregado start_url y scope
 Configurado orientation: portrait
 Display standalone para app nativa
HTML Metatags (index.html)
 theme-color para barra de navegación
 apple-mobile-web-app-capable para iOS
 mobile-web-app-capable para Android
 Viewport con user-scalable=no para PWA
Automatización
 Script PowerShell update-version.ps1:
Incrementa versión automáticamente (patch)
Actualiza package.json
Actualiza service-worker.js
Sincroniza ambos archivos
 Guía completa PWA-UPDATE-GUIDE.md
Flujo de Actualización
Desarrollador ejecuta update-version.ps1
Build y deploy de nueva versión
Usuario abre la app
Service Worker detecta nueva versión
Modal aparece automáticamente bloqueando UI
Usuario presiona "ACTUALIZAR AHORA"
Service Worker se activa
Página se recarga automáticamente
Usuario usa nueva versión
Backend (v1.0.84)
Sin cambios
Ahora la PWA se actualiza automáticamente mostrando un modal imposible de ignorar! 🚀📱
This commit is contained in:
2025-11-30 23:11:33 -03:00
parent 45ad650bac
commit a692948a6f
7 changed files with 367 additions and 3 deletions

View File

@@ -8,6 +8,56 @@ import QuestionAnswerInput from './QuestionAnswerInput'
function App() {
const [user, setUser] = useState(null)
const [loading, setLoading] = useState(true)
const [updateAvailable, setUpdateAvailable] = useState(false)
const [waitingWorker, setWaitingWorker] = useState(null)
// Detectar actualizaciones del Service Worker
useEffect(() => {
if ('serviceWorker' in navigator) {
// Registrar service worker
navigator.serviceWorker.register('/service-worker.js')
.then((registration) => {
console.log('✅ Service Worker registrado:', registration);
// Verificar si hay actualización esperando
if (registration.waiting) {
setWaitingWorker(registration.waiting);
setUpdateAvailable(true);
}
// Detectar cuando hay nueva versión instalándose
registration.addEventListener('updatefound', () => {
const newWorker = registration.installing;
console.log('🔄 Nueva versión detectada, instalando...');
newWorker.addEventListener('statechange', () => {
if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
// Hay nueva versión disponible
console.log('✨ Nueva versión lista!');
setWaitingWorker(newWorker);
setUpdateAvailable(true);
}
});
});
})
.catch((error) => {
console.error('❌ Error al registrar Service Worker:', error);
});
// Escuchar cambios de controlador (cuando se activa nueva versión)
navigator.serviceWorker.addEventListener('controllerchange', () => {
console.log('🔄 Controlador cambiado, recargando página...');
window.location.reload();
});
}
}, []);
// Función para actualizar la app
const handleUpdate = () => {
if (waitingWorker) {
waitingWorker.postMessage({ type: 'SKIP_WAITING' });
}
};
useEffect(() => {
// Verificar si hay token guardado
@@ -31,6 +81,38 @@ function App() {
return (
<Router>
<div className="min-h-screen bg-gray-50">
{/* Modal de actualización disponible */}
{updateAvailable && (
<div className="fixed inset-0 bg-black/80 flex items-center justify-center z-[9999] p-4">
<div className="bg-white rounded-2xl max-w-md w-full p-6 sm:p-8 shadow-2xl animate-bounce">
<div className="text-center">
<div className="mb-4 flex justify-center">
<div className="w-20 h-20 bg-gradient-to-r from-green-500 to-emerald-500 rounded-full flex items-center justify-center">
<span className="text-4xl">🔄</span>
</div>
</div>
<h2 className="text-2xl sm:text-3xl font-bold text-gray-900 mb-3">
¡Nueva Actualización!
</h2>
<p className="text-gray-600 mb-6 text-sm sm:text-base">
Hay una nueva versión disponible con mejoras y correcciones.
<br />
<strong className="text-indigo-600">Por favor actualiza para continuar.</strong>
</p>
<button
onClick={handleUpdate}
className="w-full py-4 px-6 bg-gradient-to-r from-indigo-600 to-purple-600 text-white text-lg sm:text-xl font-bold rounded-xl hover:from-indigo-700 hover:to-purple-700 transition-all transform hover:scale-105 shadow-lg"
>
🚀 ACTUALIZAR AHORA
</button>
<p className="text-xs text-gray-400 mt-4">
La página se recargará automáticamente
</p>
</div>
</div>
</div>
)}
{!user ? (
<LoginPage setUser={setUser} />
) : (