/* eslint-disable react-refresh/only-export-components */ import { createContext, useContext, useEffect, useState, type ReactNode, } from "react"; import { type LngLatLike } from "maplibre-gl"; type Theme = "light" | "dark"; type TableStyle = "regular" | "grouped"; type MapPositionMode = "gps" | "last"; interface MapState { center: LngLatLike; zoom: number; userLocation: LngLatLike | null; hasLocationPermission: boolean; } interface AppContextProps { theme: Theme; setTheme: React.Dispatch>; toggleTheme: () => void; tableStyle: TableStyle; setTableStyle: React.Dispatch>; toggleTableStyle: () => void; mapState: MapState; setMapCenter: (center: LngLatLike) => void; setMapZoom: (zoom: number) => void; setUserLocation: (location: LngLatLike | null) => void; setLocationPermission: (hasPermission: boolean) => void; updateMapState: (center: LngLatLike, zoom: number) => void; mapPositionMode: MapPositionMode; setMapPositionMode: (mode: MapPositionMode) => void; } // Coordenadas por defecto centradas en Vigo const DEFAULT_CENTER: LngLatLike = [42.229188855975046, -8.72246955783102]; const DEFAULT_ZOOM = 14; const AppContext = createContext(undefined); export const AppProvider = ({ children }: { children: ReactNode }) => { //#region Theme const [theme, setTheme] = useState(() => { const savedTheme = localStorage.getItem("theme"); if (savedTheme) { return savedTheme as Theme; } const prefersDark = window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches; return prefersDark ? "dark" : "light"; }); const toggleTheme = () => { setTheme((prevTheme) => (prevTheme === "light" ? "dark" : "light")); }; useEffect(() => { document.documentElement.setAttribute("data-theme", theme); localStorage.setItem("theme", theme); }, [theme]); //#endregion //#region Table Style const [tableStyle, setTableStyle] = useState(() => { const savedTableStyle = localStorage.getItem("tableStyle"); if (savedTableStyle) { return savedTableStyle as TableStyle; } return "regular"; }); const toggleTableStyle = () => { setTableStyle((prevTableStyle) => prevTableStyle === "regular" ? "grouped" : "regular", ); }; useEffect(() => { localStorage.setItem("tableStyle", tableStyle); }, [tableStyle]); //#endregion //#region Map Position Mode const [mapPositionMode, setMapPositionMode] = useState( () => { const saved = localStorage.getItem("mapPositionMode"); return saved === "last" ? "last" : "gps"; }, ); useEffect(() => { localStorage.setItem("mapPositionMode", mapPositionMode); }, [mapPositionMode]); //#endregion //#region Map State const [mapState, setMapState] = useState(() => { const savedMapState = localStorage.getItem("mapState"); if (savedMapState) { try { const parsed = JSON.parse(savedMapState); return { center: parsed.center || DEFAULT_CENTER, zoom: parsed.zoom || DEFAULT_ZOOM, userLocation: parsed.userLocation || null, hasLocationPermission: parsed.hasLocationPermission || false, }; } catch (e) { console.error("Error parsing saved map state", e); } } return { center: DEFAULT_CENTER, zoom: DEFAULT_ZOOM, userLocation: null, hasLocationPermission: false, }; }); const setMapCenter = (center: LngLatLike) => { setMapState((prev) => { const newState = { ...prev, center }; localStorage.setItem("mapState", JSON.stringify(newState)); return newState; }); }; const setMapZoom = (zoom: number) => { setMapState((prev) => { const newState = { ...prev, zoom }; localStorage.setItem("mapState", JSON.stringify(newState)); return newState; }); }; const setUserLocation = (userLocation: LngLatLike | null) => { setMapState((prev) => { const newState = { ...prev, userLocation }; localStorage.setItem("mapState", JSON.stringify(newState)); return newState; }); }; const setLocationPermission = (hasLocationPermission: boolean) => { setMapState((prev) => { const newState = { ...prev, hasLocationPermission }; localStorage.setItem("mapState", JSON.stringify(newState)); return newState; }); }; const updateMapState = (center: LngLatLike, zoom: number) => { setMapState((prev) => { const newState = { ...prev, center, zoom }; localStorage.setItem("mapState", JSON.stringify(newState)); return newState; }); }; //#endregion // Tratar de obtener la ubicación del usuario cuando se carga la aplicación si ya se había concedido permiso antes useEffect(() => { if (mapState.hasLocationPermission && !mapState.userLocation) { if (navigator.geolocation) { navigator.geolocation.getCurrentPosition( (position) => { const { latitude, longitude } = position.coords; setUserLocation([latitude, longitude]); }, (error) => { console.error("Error getting location:", error); setLocationPermission(false); }, ); } } }, [mapState.hasLocationPermission, mapState.userLocation]); return ( {children} ); }; export const useApp = () => { const context = useContext(AppContext); if (!context) { throw new Error("useApp must be used within a AppProvider"); } return context; };