fix(map): two-layer cancel token system to fully prevent orphan markers/polylines on banner close

This commit is contained in:
2026-03-04 12:04:02 -05:00
parent 6c197ba1f8
commit c5e5946738
2 changed files with 53 additions and 19 deletions

View File

@ -31,7 +31,8 @@ export const useFlujoPrincipal = () => {
map: google.maps.Map | undefined,
addCleanMarker: Function,
skipGuidedZoom = false,
onStopClick?: (stop: BusStop) => void
onStopClick?: (stop: BusStop) => void,
cancelToken?: { cancelled: boolean } // token de cancelación pasado desde el llamador
) => {
if (!map) return
@ -50,6 +51,13 @@ export const useFlujoPrincipal = () => {
paradasExistentes.length > 0 ? Promise.resolve(paradasExistentes) : routeStore.loadRouteStops(_ruta.id)
]);
// Guard: abandono si el token fue cancelado o la ruta ya no está activa
if (cancelToken?.cancelled || routeStore.selectedRouteId !== _ruta.id) {
console.log('SIBU | procesarSeleccionDeRuta abortado tras Promise.allSettled (ruta ya no activa o cancelada)');
limpiarMapa(); // limpiar cualquier polyline ya dibujada
return;
}
// ── PASO 3: Asignación Segura ──
let ubicacion: { lat: number, lng: number } | null = null;
if (ubicacionRes.status === 'fulfilled') {
@ -58,12 +66,6 @@ export const useFlujoPrincipal = () => {
console.warn('SIBU | GPS falló o fue denegado');
}
// Guard contra race condition: si el usuario cerró el banner mientras cargaba
if (routeStore.selectedRouteId !== _ruta.id) {
console.log('SIBU | Carga abortada: La ruta ya no está seleccionada.');
return;
}
let paradas: BusStop[] = [];
if (paradasRes.status === 'fulfilled') {
// Si loadRouteStops no devolviera los datos directamente, los tomamos del store
@ -86,6 +88,7 @@ export const useFlujoPrincipal = () => {
// Si no detectamos GPS, trazamos la ruta completa sin zoom guiado
if (!ubicacion) {
await trazarRuta(paradasFormateadas, map, false);
if (cancelToken?.cancelled) { limpiarMapa(); return; }
const bounds = new google.maps.LatLngBounds()
paradasFormateadas.forEach(p => bounds.extend(new google.maps.LatLng(p.latitud, p.longitud)))
map.fitBounds(bounds, { top: 100, bottom: 100, left: 60, right: 60 })
@ -93,11 +96,19 @@ export const useFlujoPrincipal = () => {
}
// ── PASO 4: Dibujar y Renderizar ──
if (routeStore.selectedRouteId !== _ruta.id) return;
if (cancelToken?.cancelled || routeStore.selectedRouteId !== _ruta.id) {
limpiarMapa();
return;
}
// Dibujar ruta completa (fondo)
await trazarRuta(paradasFormateadas, map, true);
if (cancelToken?.cancelled || routeStore.selectedRouteId !== _ruta.id) {
limpiarMapa(); // limpiar polylines ya dibujadas
return;
}
if (skipGuidedZoom) {
const bounds = new google.maps.LatLngBounds()
paradasFormateadas.forEach(p => bounds.extend(new google.maps.LatLng(p.latitud, p.longitud)))
@ -108,7 +119,10 @@ export const useFlujoPrincipal = () => {
await encontrarParadaCercana(ubicacion, paradas, map)
const paradaCercanaFound = paradaCercana.value
if (routeStore.selectedRouteId !== _ruta.id) return;
if (cancelToken?.cancelled || routeStore.selectedRouteId !== _ruta.id) {
limpiarMapa();
return;
}
if (!paradaCercanaFound) return;
// Dibujar tramo relevante
@ -125,6 +139,12 @@ export const useFlujoPrincipal = () => {
}
}
// Guard final antes de pintar markers
if (cancelToken?.cancelled || routeStore.selectedRouteId !== _ruta.id) {
limpiarMapa();
return;
}
// Renderizado Condicional de Marcadores
paradasFormateadas.forEach((p, i) => {
const esCercana = i === idx;