/** Pinia store for route management */ import { defineStore } from 'pinia' import { ref, computed } from 'vue' import type { Route, BusStop } from '@/types' import { routesService } from '@/services/routesService' export const useRouteStore = defineStore('route', () => { const selectedRouteId = ref(null) const selectedRouteName = ref(null) const selectedRouteStops = ref([]) const allRoutes = ref([]) const isLoadingRoutes = ref(false) const isLoadingStops = ref(false) const error = ref(null) const wasSelectedFromMap = ref(false) const lastFetched = ref(0) // ⚡ CACHÉ ESTÁTICO RUTAS const stopsCache = ref>(new Map()) // ⚡ CACHÉ ESTÁTICO PARADAS const hasSelectedRoute = computed(() => selectedRouteId.value !== null && selectedRouteName.value !== null) // Safety: nunca más de 12s cargando (protección contra thread congelado por OS al apagar pantalla) let _routesSafetyTimer: ReturnType | null = null let _stopsSafetyTimer: ReturnType | null = null function _startRoutesSafety() { if (_routesSafetyTimer) clearTimeout(_routesSafetyTimer) _routesSafetyTimer = setTimeout(() => { if (isLoadingRoutes.value) { console.warn('SIBU | routeStore: routes safety timeout — reseteando isLoadingRoutes') isLoadingRoutes.value = false } }, 12000) } function _startStopsSafety() { if (_stopsSafetyTimer) clearTimeout(_stopsSafetyTimer) _stopsSafetyTimer = setTimeout(() => { if (isLoadingStops.value) { console.warn('SIBU | routeStore: stops safety timeout — reseteando isLoadingStops') isLoadingStops.value = false } }, 12000) } async function loadRoutes(filters?: { originCity?: string, destinationCity?: string }, force = false, isBackground = false) { const CACHE_TIME = 1000 * 60 * 15; // 15 minutos const now = Date.now(); // Guard: Si ya se están cargando rutas y NO estamos en background, omitir // Si estamos en background, podemos sobreescribir la carga sin mostrar spinner if (isLoadingRoutes.value && !isBackground) return; // Si no forzamos, no hay filtros raros, ya tenemos rutas y aún no expira el caché, omitir llamada. // Se respeta en background también porque descargar docenas de rutas traba el Javascript en móviles. if (!force && !filters && allRoutes.value.length > 0 && (now - lastFetched.value < CACHE_TIME)) { return } if (!isBackground) { isLoadingRoutes.value = true _startRoutesSafety() } error.value = null try { allRoutes.value = await routesService.getAllRoutes(filters) if (!filters) lastFetched.value = now; // Solo actualizar timer si es un request general limio } catch (e) { error.value = e instanceof Error ? e.message : 'Failed to load routes' console.error('Error loading routes:', e) } finally { if (!isBackground) { if (_routesSafetyTimer) { clearTimeout(_routesSafetyTimer); _routesSafetyTimer = null } isLoadingRoutes.value = false } } } async function loadRouteStops(routeId: string, force = false): Promise { const CACHE_TIME = 1000 * 60 * 15; // 15 minutos const now = Date.now(); if (stopsCache.value.has(routeId) && !force) { const cacheEntry = stopsCache.value.get(routeId)!; if (now - cacheEntry.fetchedAt < CACHE_TIME) { selectedRouteStops.value = cacheEntry.stops; return cacheEntry.stops; } } if (isLoadingStops.value) return []; isLoadingStops.value = true error.value = null _startStopsSafety() try { const stops = await routesService.getRouteStops(routeId) selectedRouteStops.value = stops stopsCache.value.set(routeId, { fetchedAt: now, stops }) return stops } catch (e) { error.value = e instanceof Error ? e.message : 'Failed to load route stops' console.error('Error loading route stops:', e) selectedRouteStops.value = [] return [] } finally { if (_stopsSafetyTimer) { clearTimeout(_stopsSafetyTimer); _stopsSafetyTimer = null } isLoadingStops.value = false } } async function selectRoute(routeId: string, routeName: string) { if (selectedRouteId.value === routeId) return selectedRouteId.value = routeId selectedRouteName.value = routeName selectedRouteStops.value = [] // Limpia para forzar recarga atómica en la vista } // Watcher manual para wasSelectedFromMap ya que se cambia en varios sitios function setWasSelectedFromMap(val: boolean) { wasSelectedFromMap.value = val } function clearSelection() { selectedRouteId.value = null selectedRouteName.value = null selectedRouteStops.value = [] wasSelectedFromMap.value = false } return { selectedRouteId, selectedRouteName, selectedRouteStops, allRoutes, isLoadingRoutes, isLoadingStops, error, wasSelectedFromMap, hasSelectedRoute, loadRoutes, loadRouteStops, selectRoute, setWasSelectedFromMap, clearSelection, } }, { persist: { pick: ['selectedRouteId', 'selectedRouteName', 'wasSelectedFromMap', 'allRoutes'] } })