fix(frontend): post-change-password cookie timing + defensive 401 fallback
CI / Build Native (push) Successful in 7m45s

Dos cambios para destrabar el flujo post-change-password:

1. AuthContext.changePassword: despues de setUser(u), hacer un round-trip
   a api.me() para confirmar que la nueva cookie ya es utilizable. Sin
   este paso, React monta AuthenticatedApp tan rapido que el primer
   fetch desde usePersistedState.load() corre antes de que el browser
   haya propagado el Set-Cookie (race condition reportado en Chrome).

2. usePersistedState.load: en 401, caer en makeDefaultAppState() en lugar
   de setStateInternal(null). Antes dejaba al usuario varado en un
   <LoadingScreen>Cargando tu estado...</LoadingScreen> para siempre. Ahora
   ve la app vacia con SaveIndicator, y al primer cambio real el
   auto-save sube el estado al server.
This commit is contained in:
2026-08-15 03:27:31 -04:00
parent ec5ac6735c
commit b161710952
2 changed files with 7 additions and 1 deletions
+5
View File
@@ -54,6 +54,11 @@ export function AuthProvider({ children }: { children: ReactNode }) {
const changePassword = useCallback(async (currentPassword: string, newPassword: string) => { const changePassword = useCallback(async (currentPassword: string, newPassword: string) => {
const u = await api.changePassword(currentPassword, newPassword) const u = await api.changePassword(currentPassword, newPassword)
setUser(u) setUser(u)
// El Set-Cookie de la respuesta a veces no está visible para el fetch
// siguiente en el mismo microtask (Chrome/Firefox). Confirmamos con
// un round-trip al backend antes de soltar el control, asi React
// monta AuthenticatedApp con la cookie ya lista para los fetches internos.
await api.me()
}, []) }, [])
return ( return (
+2 -1
View File
@@ -29,7 +29,8 @@ export function usePersistedState(): UsePersistedStateResult {
setStateInternal(loaded ?? makeDefaultAppState()) setStateInternal(loaded ?? makeDefaultAppState())
} catch (err) { } catch (err) {
if (err instanceof ApiError && err.status === 401) { if (err instanceof ApiError && err.status === 401) {
setStateInternal(null) // No bloquear la UI en 401. Cargar default state.
setStateInternal(makeDefaultAppState())
} else { } else {
setErrorMessage(err instanceof Error ? err.message : 'Error cargando estado') setErrorMessage(err instanceof Error ? err.message : 'Error cargando estado')
} }