fix: version 2.8.2 -- overlay de progreso al actualizar + fix mountedRef en StrictMode
Agrega un overlay de pantalla completa (circulo girando + porcentaje estimado + "Actualizando...") que bloquea todo clic mientras corre la auto-actualizacion, y recarga la pagina sola al terminar. De paso corrige un bug real: el useEffect de mountedRef no reactivaba el flag en su setup, asi que con React.StrictMode (dev) el polling de estado nunca llegaba a correr -- no afectaba produccion (StrictMode no duplica efectos ahi) pero ocultaba el problema al probar en dev. Tambien se corrigen un par de voseos que quedaron en los textos de Actualizaciones. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -16,27 +16,44 @@ export default function UpdaterPanel() {
|
||||
const { data: status, loading, error, reload, setData } = useAsync(getUpdateStatus, []);
|
||||
const runAction = useAction();
|
||||
const [applying, setApplying] = useState(false);
|
||||
const [progressPct, setProgressPct] = useState(0);
|
||||
const mountedRef = useRef(true);
|
||||
useEffect(() => () => { mountedRef.current = false; }, []);
|
||||
// Setup explicito (no solo el cleanup): con React.StrictMode en dev el efecto se dispara
|
||||
// mount->cleanup->mount de una, y si el setup no reactiva el flag queda en false para siempre
|
||||
// aunque el componente SI este montado -- pollUntilUpToDate nunca correria.
|
||||
useEffect(() => {
|
||||
mountedRef.current = true;
|
||||
return () => { mountedRef.current = false; };
|
||||
}, []);
|
||||
|
||||
// El servidor se reinicia a mitad de la actualizacion (backend y frontend), asi que durante
|
||||
// ese rato las llamadas van a fallar por un momento -- se ignoran y se sigue insistiendo.
|
||||
// Cuando confirma que ya esta al dia, el boton "Actualizar" desaparece solo (esta gateado por
|
||||
// !status.upToDate mas abajo) sin depender de que la pestaña se recargue.
|
||||
// ese rato las llamadas van a fallar por un momento -- se ignoran y se sigue insistiendo. El
|
||||
// porcentaje es una estimacion sobre el tope de espera (no hay progreso real del script), pero
|
||||
// alcanza para que la persona sepa que sigue avanzando y no quede mirando una pantalla muerta.
|
||||
// Al confirmar que ya esta al dia, se recarga la pestaña sola para traer el build nuevo.
|
||||
async function pollUntilUpToDate() {
|
||||
for (let i = 0; i < POLL_MAX_ATTEMPTS && mountedRef.current; i++) {
|
||||
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
|
||||
if (!mountedRef.current) return;
|
||||
setProgressPct(Math.min(95, Math.round(((i + 1) / POLL_MAX_ATTEMPTS) * 100)));
|
||||
try {
|
||||
const result = await getUpdateStatus();
|
||||
if (!mountedRef.current) return;
|
||||
setData(result);
|
||||
if (result.upToDate) { setApplying(false); return; }
|
||||
if (result.upToDate) {
|
||||
setProgressPct(100);
|
||||
await new Promise((resolve) => setTimeout(resolve, 600));
|
||||
window.location.reload();
|
||||
return;
|
||||
}
|
||||
} catch {
|
||||
// backend reiniciandose -- se ignora y se reintenta en el proximo ciclo
|
||||
}
|
||||
}
|
||||
if (mountedRef.current) setApplying(false);
|
||||
if (mountedRef.current) {
|
||||
setApplying(false);
|
||||
pushToast({ tone: 'warning', message: t('updater_apply_timeout') });
|
||||
}
|
||||
}
|
||||
|
||||
function confirmApply() {
|
||||
@@ -49,6 +66,7 @@ export default function UpdaterPanel() {
|
||||
{
|
||||
label: t('updater_apply_confirm_ok'), variant: 'danger',
|
||||
onClick: () => runAction(async () => {
|
||||
setProgressPct(0);
|
||||
setApplying(true);
|
||||
await applyUpdate();
|
||||
pushToast({ tone: 'success', message: t('updater_apply_started') });
|
||||
@@ -60,6 +78,18 @@ export default function UpdaterPanel() {
|
||||
|
||||
return (
|
||||
<div>
|
||||
{applying && (
|
||||
<div className="update-overlay" role="alert" aria-live="assertive">
|
||||
<div className="update-spinner-wrap">
|
||||
<div className="update-spinner-ring" />
|
||||
<div className="update-spinner-center">
|
||||
<div className="update-spinner-label">{t('updater_applying')}</div>
|
||||
<div className="update-spinner-pct">{progressPct}%</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 12, flexWrap: 'wrap' }}>
|
||||
<Button variant="standard" icon={RefreshCw} onClick={reload} disabled={loading || applying}>
|
||||
{t('updater_check')}
|
||||
|
||||
Reference in New Issue
Block a user