215 lines
8.3 KiB
TypeScript
215 lines
8.3 KiB
TypeScript
import { ref } from 'vue'
|
|
import { useMapState } from './useMapState'
|
|
import { useDirectionsRoute } from './useDirectionsRoute'
|
|
import { useParadaCercana } from './useParadaCercana'
|
|
import type { BusStop } from '@/types'
|
|
import { useRouteStore } from '@/stores/route'
|
|
|
|
export const useFlujoPrincipal = () => {
|
|
const { limpiarMapa } = useMapState()
|
|
const paradaCercanaInst = useParadaCercana()
|
|
const { encontrarParadaCercana, paradaCercana } = paradaCercanaInst
|
|
const { trazarRuta } = useDirectionsRoute()
|
|
const cargando = ref(false)
|
|
const errorMsg = ref('')
|
|
|
|
// Simulated obtenerUbicacion (since it was just navigator.geolocation)
|
|
const obtenerUbicacion = (): Promise<{ lat: number, lng: number }> => {
|
|
return new Promise((resolve, reject) => {
|
|
if (!navigator.geolocation) return reject(new Error('No geolocation'))
|
|
navigator.geolocation.getCurrentPosition(
|
|
(pos) => resolve({ lat: pos.coords.latitude, lng: pos.coords.longitude }),
|
|
(err) => reject(err),
|
|
{ enableHighAccuracy: true, timeout: 8000, maximumAge: 30000 }
|
|
)
|
|
})
|
|
}
|
|
|
|
const procesarSeleccionDeRuta = async (
|
|
_ruta: { id: string },
|
|
paradasExistentes: BusStop[],
|
|
map: google.maps.Map | undefined,
|
|
addCleanMarker: Function,
|
|
skipGuidedZoom = false,
|
|
onStopClick?: (stop: BusStop) => void,
|
|
cancelToken?: { cancelled: boolean } // token de cancelación pasado desde el llamador
|
|
) => {
|
|
if (!map) return
|
|
|
|
try {
|
|
// ── PASO 1: Inicio Atómico ──
|
|
cargando.value = true
|
|
limpiarMapa()
|
|
const routeStore = useRouteStore()
|
|
|
|
// ── PASO 2: Sincronización (Promise.all) ──
|
|
// Disparamos Supabase (paradas) y GPS al mismo tiempo
|
|
console.log('SIB | Iniciando carga síncrona de Datos + GPS...');
|
|
|
|
const [ubicacionRes, paradasRes] = await Promise.allSettled([
|
|
obtenerUbicacion(),
|
|
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('SIB | 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') {
|
|
ubicacion = ubicacionRes.value;
|
|
} else {
|
|
console.warn('SIB | GPS falló o fue denegado');
|
|
}
|
|
|
|
let paradas: BusStop[] = [];
|
|
if (paradasRes.status === 'fulfilled') {
|
|
// Si loadRouteStops no devolviera los datos directamente, los tomamos del store
|
|
paradas = paradasRes.value || routeStore.selectedRouteStops;
|
|
}
|
|
|
|
const paradasFormateadas = paradas.map((p, i) => ({
|
|
id: typeof p.id === 'string' ? parseInt(p.id) || i : p.id || i,
|
|
nombre: p.name,
|
|
latitud: p.latitude,
|
|
longitud: p.longitude,
|
|
orden: i
|
|
}));
|
|
|
|
if (paradasFormateadas.length < 2) {
|
|
console.warn('SIB | No hay suficientes paradas para trazar ruta');
|
|
return;
|
|
}
|
|
|
|
// 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 })
|
|
return;
|
|
}
|
|
|
|
// ── PASO 4: Dibujar y Renderizar ──
|
|
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)))
|
|
map.fitBounds(bounds, { top: 100, bottom: 100, left: 60, right: 60 })
|
|
return;
|
|
}
|
|
|
|
await encontrarParadaCercana(ubicacion, paradas, map)
|
|
const paradaCercanaFound = paradaCercana.value
|
|
|
|
if (cancelToken?.cancelled || routeStore.selectedRouteId !== _ruta.id) {
|
|
limpiarMapa();
|
|
return;
|
|
}
|
|
if (!paradaCercanaFound) return;
|
|
|
|
// Dibujar tramo relevante
|
|
const idx = paradasFormateadas.findIndex(p => {
|
|
const samePos = Math.abs(p.longitud - paradaCercanaFound.longitude) < 0.0001 &&
|
|
Math.abs(p.latitud - paradaCercanaFound.latitude) < 0.0001;
|
|
return samePos || p.nombre === paradaCercanaFound.name;
|
|
});
|
|
|
|
if (idx !== -1) {
|
|
const tramoRelevante = paradasFormateadas.slice(idx)
|
|
if (tramoRelevante.length > 1) {
|
|
await trazarRuta(tramoRelevante, map, false)
|
|
}
|
|
}
|
|
|
|
// 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;
|
|
const esPasada = idx !== -1 && i < idx;
|
|
|
|
addCleanMarker(
|
|
{ lat: p.latitud, lng: p.longitud },
|
|
p.nombre,
|
|
esCercana ? 'cercana' : (esPasada ? 'pasada' : 'normal'),
|
|
() => {
|
|
if (onStopClick && paradas[i]) {
|
|
onStopClick(paradas[i]);
|
|
}
|
|
}
|
|
);
|
|
});
|
|
|
|
hacerZoomAlTramoRelevante(ubicacion, paradaCercanaFound, map)
|
|
|
|
} catch (error) {
|
|
console.error('SIB | Error procesando ruta:', error)
|
|
errorMsg.value = 'No se pudo cargar la ruta'
|
|
} finally {
|
|
// Apagar estado de carga
|
|
cargando.value = false
|
|
}
|
|
}
|
|
|
|
// ZOOM QUE MUESTRA USUARIO Y PARADA CERCANA:
|
|
const hacerZoomAlTramoRelevante = (
|
|
ubicacion: { lat: number, lng: number },
|
|
paradaCercana: BusStop,
|
|
map: google.maps.Map
|
|
) => {
|
|
const bounds = new google.maps.LatLngBounds()
|
|
|
|
// Incluir solo estos dos puntos clave
|
|
bounds.extend(new google.maps.LatLng(ubicacion.lat, ubicacion.lng))
|
|
bounds.extend(
|
|
new google.maps.LatLng(paradaCercana.latitude, paradaCercana.longitude)
|
|
)
|
|
|
|
// Ajustar zoom con padding generoso para que se vean bien
|
|
map.fitBounds(bounds, {
|
|
top: 100, // espacio para la navbar
|
|
bottom: 200, // espacio para el bottom sheet de ETA
|
|
left: 60,
|
|
right: 60
|
|
})
|
|
|
|
// Asegurar zoom mínimo para que se vea el contexto de la calle
|
|
google.maps.event.addListenerOnce(
|
|
map,
|
|
'bounds_changed',
|
|
() => {
|
|
if ((map.getZoom() ?? 0) > 17) {
|
|
map.setZoom(17) // no acercar demasiado
|
|
}
|
|
if ((map.getZoom() ?? 0) < 14) {
|
|
map.setZoom(14) // no alejar demasiado
|
|
}
|
|
}
|
|
)
|
|
}
|
|
|
|
return { procesarSeleccionDeRuta, cargando, errorMsg }
|
|
}
|