✅ 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:
@@ -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} />
|
||||
) : (
|
||||
|
||||
Reference in New Issue
Block a user