1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
|
import { type LngLatLike } from "maplibre-gl";
import {
createContext,
useContext,
useEffect,
useState,
type ReactNode,
} from "react";
import { APP_CONSTANTS } from "~/config/constants";
interface MapState {
center: LngLatLike;
zoom: number;
userLocation: LngLatLike | null;
hasLocationPermission: boolean;
}
interface MapContextProps {
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;
}
const MapContext = createContext<MapContextProps | undefined>(undefined);
export const MapProvider = ({ children }: { children: ReactNode }) => {
const [mapState, setMapState] = useState<MapState>(() => {
const savedMapState = localStorage.getItem("mapState");
if (savedMapState) {
try {
const parsed = JSON.parse(savedMapState);
// Validate that the saved center is valid if needed, or just trust it.
// We might want to ensure we have a fallback if the region changed while the app was closed?
// But for now, let's stick to the existing logic.
return {
center: parsed.center || APP_CONSTANTS.defaultCenter,
zoom: parsed.zoom || APP_CONSTANTS.defaultZoom,
userLocation: parsed.userLocation || null,
hasLocationPermission: parsed.hasLocationPermission || false,
};
} catch (e) {
console.error("Error parsing saved map state", e);
}
}
return {
center: APP_CONSTANTS.defaultCenter,
zoom: APP_CONSTANTS.defaultZoom,
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;
});
};
// Try to get user location on load if permission was granted
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 (
<MapContext.Provider
value={{
mapState,
setMapCenter,
setMapZoom,
setUserLocation,
setLocationPermission,
updateMapState,
}}
>
{children}
</MapContext.Provider>
);
};
export const useMap = () => {
const context = useContext(MapContext);
if (!context) {
throw new Error("useMap must be used within a MapProvider");
}
return context;
};
|