fix(fires): el mapa de /fires ya no se queda en gris
All checks were successful
build-image / test (push) Successful in 2m22s
build-image / build (push) Successful in 11m25s

Causa raíz, encontrada reproduciéndolo con el bundle de producción en local
—con el servidor de desarrollo NO se ve, y por eso llevaba semanas dándose por
un artefacto del navegador sin pantalla:

    if ((centerStored !== [0, 0] || geolocation.get()) && geoInit) {
      center.set(centerStored || geolocation.get());
      geoInit = false;
    }

`centerStored !== [0, 0]` compara contra un array recién creado: es SIEMPRE
cierto. En un navegador nuevo —sin centro en localStorage y con la
geolocalización todavía sin resolver— eso hacía `center.set(undefined)` y dejaba
`geoInit` en false, o sea para siempre. Un `<MapContainer>` sin `center` no
recibe `setView`, y un mapa de Leaflet sin vista no carga capa base ni teselas
ni dispara `whenReady`, con lo que tampoco se creaba la suscripción por
viewport. En desarrollo no pasaba porque la geolocalización llegaba a tiempo.

Y de paso el "Actualizando…" que no se quitaba nunca: las suscripciones se
creaban dentro de un `Tracker.autorun` ANIDADO en el withTracker. La computación
de fuera no dependía de `mapSize`, así que `loading` se calculaba con
`subscription` a undefined y no volvía a recalcularse; además cada pasada del
withTracker dejaba otro autorun sin parar. Ahora las suscripciones se crean en
la propia computación reactiva, que es donde Meteor sabe pararlas al invalidarse.

MapReady, además, vigila el contenedor con un ResizeObserver: su comprobación de
tamaño solo podía reintentar con el evento `resize` de Leaflet, que no se emite
si nadie llama a invalidateSize(). No era la causa de esto, pero hacía que la
comprobación no sirviera de nada.

La suite e2e deja de usar selectores por id: TestUtils.testId() los devuelve solo
en desarrollo, así que contra el bundle de producción —lo que levanta el job
nocturno— no existían y la suite entera habría fallado esta noche.

Verificado contra bundle de producción: /fires pasa de 0 teselas y 0 capas a 12
teselas, capa base y las dos suscripciones por viewport, sin "Actualizando…".
90 tests de servidor, 16 e2e y smoke REST byte-idéntico.
This commit is contained in:
vjrj 2026-08-03 19:59:30 +02:00
parent b0ca838788
commit bba2ad5523
6 changed files with 154 additions and 109 deletions

View file

@ -8,7 +8,6 @@ import { ButtonGroup, Row, Col, Form } from 'react-bootstrap';
import { Meteor } from 'meteor/meteor';
import { ReactiveVar } from 'meteor/reactive-var';
import { withTracker } from 'meteor/react-meteor-data';
import { Tracker } from 'meteor/tracker';
import { Helmet } from 'react-helmet-async';
import { Trans, withTranslation } from 'react-i18next';
import { MapContainer } from 'react-leaflet';
@ -441,47 +440,59 @@ export default withTranslation()(withTracker(() => {
}
// Disable, because this increase the number of fires by one
// Meteor.subscribe('lastFireDetected');
Tracker.autorun(() => {
if ((centerStored !== [0, 0] || geolocation.get()) && geoInit) {
center.set(centerStored || geolocation.get());
// console.log(`Geolocation ${geolocation.get()}`);
geoInit = false;
}
if (mapSize.get() && mapSize.get()[0].lng && mapSize.get()[1].lat) {
subscription = Meteor.subscribe(
'activefiresmyloc',
mapSize.get()[0].lng,
mapSize.get()[0].lat,
mapSize.get()[1].lng,
mapSize.get()[1].lat,
marks.get() && zoom.get() >= MAXZOOM
);
subscriptionUnion = Meteor.subscribe(
'activefiresunionmyloc',
mapSize.get()[0].lng,
mapSize.get()[0].lat,
mapSize.get()[1].lng,
mapSize.get()[1].lat,
false
);
alertSubscription = Meteor.subscribe(
'fireAlerts',
mapSize.get()[0].lng,
mapSize.get()[0].lat,
mapSize.get()[1].lng,
mapSize.get()[1].lat
);
/* if (withIndustries) {
Meteor.subscribe(
'industriesMyloc',
mapSize.get()[0].lng,
mapSize.get()[0].lat,
mapSize.get()[1].lng,
mapSize.get()[1].lat
);
} */
}
});
// ⚠️ Antes esto vivía dentro de un `Tracker.autorun` ANIDADO en el propio
// withTracker, y eso tenía dos problemas. Uno: la computación de fuera no
// dependía de `mapSize`, así que cuando el mapa por fin publicaba su viewport
// y se creaban las suscripciones, `loading` se había calculado ya con
// `subscription` a undefined — y no volvía a recalcularse, con lo que la
// página se quedaba con el "Actualizando…" puesto para siempre. Dos: cada
// pasada del withTracker creaba OTRO autorun que nadie paraba.
//
// Aquí ya estamos dentro de una computación reactiva: leyendo `mapSize.get()`
// la dependencia queda registrada, y las suscripciones creadas dentro se
// paran solas cuando la computación se invalida, que es justo lo que se
// quiere al cambiar el viewport.
//
// La condición del centro era `(centerStored !== [0, 0] || geolocation.get()) && geoInit`,
// y `centerStored !== [0, 0]` compara contra un array recién creado: SIEMPRE
// cierto. En un navegador nuevo —sin centro guardado y sin geolocalización
// todavía— hacía `center.set(undefined)` y dejaba `geoInit` en false, o sea
// para siempre. Un `<MapContainer>` sin `center` no recibe `setView`, y un
// mapa de Leaflet sin vista no carga capa base ni teselas ni dispara
// `whenReady`. Ese era el "mapa gris" de /fires, que solo se veía en
// producción porque en desarrollo la geolocalización llegaba a tiempo.
const nextCenter = centerStored || geolocation.get();
if (geoInit && nextCenter) {
center.set(nextCenter);
geoInit = false;
}
const bounds = mapSize.get();
if (bounds && bounds[0].lng && bounds[1].lat) {
subscription = Meteor.subscribe(
'activefiresmyloc',
bounds[0].lng,
bounds[0].lat,
bounds[1].lng,
bounds[1].lat,
marks.get() && zoom.get() >= MAXZOOM
);
subscriptionUnion = Meteor.subscribe(
'activefiresunionmyloc',
bounds[0].lng,
bounds[0].lat,
bounds[1].lng,
bounds[1].lat,
false
);
alertSubscription = Meteor.subscribe(
'fireAlerts',
bounds[0].lng,
bounds[0].lat,
bounds[1].lng,
bounds[1].lat
);
}
Meteor.subscribe('activefirestotal');
Meteor.subscribe('activefiresuniontotal');