fix: implementar manejo correcto de modo offline
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 24s

Problema crítico anterior:
Cuando se perdía conexión, el sistema marcaba como "no autenticado"
y DESREGISTRABA el Service Worker, destruyendo todo el contenido
cacheado y volviendo la PWA inútil offline.

Solución implementada:

1. useAuth.ts:
   - Agregar estado isOffline con listeners de navigator.onLine
   - Detectar offline ANTES de marcar como no autenticado
   - En checkAuth: si offline, mantener último estado conocido
   - En markUnauthenticated: NO ejecutar si estamos offline
   - Nuevo authStatus: 'offline' cuando sin conexión
   - NO desregistrar SW cuando estamos offline

2. AuthIndicator.client.vue:
   - Importar isOffline del composable
   - Agregar icono WifiOff para estado offline
   - Agregar textos: "Offline" / "Sin conexión. Puedes usar contenido guardado"
   - Estilos naranja para estado offline
   - En watchers: NO llamar markUnauthenticated si offline
   - handleClick: ignorar clicks en modo offline

Ahora offline funciona correctamente:
 Mantiene último estado de autenticación conocido
 NO desregistra Service Worker
 Contenido cacheado permanece accesible
 Indicador visual claro (naranja) de modo offline
 La PWA es totalmente funcional sin conexión
This commit is contained in:
2025-10-17 04:21:31 -06:00
parent 40945dc634
commit eedff715d4
2 changed files with 89 additions and 11 deletions

View File

@@ -60,7 +60,7 @@
<script setup>
import { ref, computed, onMounted, onUnmounted, watch } from 'vue'
import { UserCheck, UserX, Loader, ShieldAlert, ChevronDown, RefreshCw, LogOut } from 'lucide-vue-next'
import { UserCheck, UserX, Loader, ShieldAlert, ChevronDown, RefreshCw, LogOut, WifiOff } from 'lucide-vue-next'
import { useAuth } from '~/composables/useAuth'
import { useMusicStore } from '~/stores/music'
@@ -68,6 +68,7 @@ const {
isAuthenticated,
authChecked,
isCheckingAuth,
isOffline,
authStatus,
userInfo,
checkAuth,
@@ -124,6 +125,12 @@ watch(() => musicStore.error, (error, oldError) => {
if (error && error !== oldError) {
console.log('[AuthIndicator] Music store error detected:', error)
// Si estamos offline, NO marcar como no autenticado
if (isOffline.value) {
console.log('[AuthIndicator] Ignoring error - offline mode')
return
}
// Detectar errores de autenticación (múltiples formas)
const isAuthError =
error.includes('401') ||
@@ -144,6 +151,12 @@ watch(() => musicStore.error, (error, oldError) => {
// Watch loading state - cuando termina de cargar, verificar si hubo errores de auth
watch(() => musicStore.loading, (loading, wasLoading) => {
if (wasLoading && !loading && musicStore.error) {
// Si estamos offline, NO marcar como no autenticado
if (isOffline.value) {
console.log('[AuthIndicator] Ignoring loading error - offline mode')
return
}
// Terminó de cargar con error, verificar si es de auth
const error = musicStore.error
const isAuthError =
@@ -169,6 +182,8 @@ const iconComponent = computed(() => {
return UserCheck
case 'unauthenticated':
return UserX
case 'offline':
return WifiOff
default:
return ShieldAlert
}
@@ -182,6 +197,8 @@ const statusText = computed(() => {
return 'Conectado'
case 'unauthenticated':
return 'Reautenticar'
case 'offline':
return 'Offline'
default:
return 'Verificando...'
}
@@ -195,6 +212,8 @@ const tooltipText = computed(() => {
return 'Estás autenticado. Click para verificar estado.'
case 'unauthenticated':
return 'Sesión expirada. Click para iniciar sesión de nuevo.'
case 'offline':
return 'Sin conexión. Puedes usar contenido guardado offline.'
default:
return 'Verificando estado de autenticación...'
}
@@ -228,7 +247,11 @@ const closeDropdown = () => {
const handleClick = () => {
if (isCheckingAuth.value) return
if (authStatus.value === 'unauthenticated') {
if (authStatus.value === 'offline') {
// En modo offline, no hacer nada (ya no se puede autenticar)
console.log('[AuthIndicator] Click ignored - offline mode')
return
} else if (authStatus.value === 'unauthenticated') {
// Forzar reload de la página para que Authentik intercepte
triggerAuth()
} else if (authStatus.value === 'authenticated') {
@@ -367,6 +390,16 @@ onUnmounted(() => {
--status-color: #3b82f6;
}
.auth-indicator.offline {
--status-color: #f59e0b;
border-color: rgba(245, 158, 11, 0.3);
}
.auth-indicator.offline:hover {
border-color: rgba(245, 158, 11, 0.5);
box-shadow: 0 4px 12px rgba(245, 158, 11, 0.2);
}
/* Animation for loading state */
@keyframes spin {
from {