MapEvents.res
1let fetchFeatureStats = (
2 f: Maplibre.feature,
3 event: Maplibre.mapLayerMouseEvent,
4 waypoints: array<Waypoint.attributes>,
5): promise<option<Jsx.element>> => {
6 let hash = f.source->String.replace("route-", "")
7 Log.debug(`[MapInstance] Got mouseover hash ${hash}`)
8
9 let url = `/route/${hash}.geojson`
10 Log.debug(`[MapInstance] Fetching from URL: ${url}`)
11
12 Fetch.get(url)
13 ->Fetch.onResponse(
14 ~onSuccess=json => {
15 Log.debug(`[MapInstance] Successfully fetched geojson for ${hash}`)
16 let stats = Geo.getStats(event.lngLat->Geo.lngLatToPoint, hash, waypoints, json->Obj.magic)
17 Console.log2("[MapInstance] Calculated stats:", stats)
18 Some(<Stats dist={stats.dist} time={stats.time} waypoints={waypoints} />)
19 },
20 ~onError=(status, msg) => {
21 Log.error(
22 `[MapInstance] Failed to fetch geojson for ${hash}`,
23 ~kv={
24 "status": status->Int.toString,
25 "message": msg,
26 "url": url,
27 },
28 )
29 None
30 },
31 )
32 ->Fetch.send
33}
34
35let getStats = (
36 features: array<Maplibre.feature>,
37 event: Maplibre.mapLayerMouseEvent,
38 waypoints: array<Waypoint.attributes>,
39 popup: Preact.Signal.t<option<Jsx.element>>,
40) => {
41 Log.debug(`[MapInstance] Fetching stats for ${features->Array.length->Int.toString} features`)
42
43 // Fetch all features concurrently
44 let promises = features->Array.map(f => fetchFeatureStats(f, event, waypoints))
45 Array.reverse(promises)
46
47 let _ = (
48 async () => {
49 let results = await Promise.all(promises)
50
51 // Filter out None results (failed fetches)
52 let statsElements = results->Array.filterMap(x => x)
53
54 if statsElements->Array.length > 0 {
55 Log.debug(
56 `[MapInstance] Successfully loaded ${statsElements
57 ->Array.length
58 ->Int.toString} of ${features->Array.length->Int.toString} stats`,
59 )
60
61 popup->Preact.Signal.set(
62 Some(
63 <Maplibre.Popup
64 longitude={event.lngLat.lng}
65 latitude={event.lngLat.lat}
66 onClose={_evt => popup->Preact.Signal.set(None)}
67 closeButton={false}
68 maxWidth={Window.isMobile->Preact.Signal.get ? "300px" : "600px"}
69 >
70 <div className="flex flex-col">
71 {switch statsElements {
72 | [stat] => stat
73 | stats =>
74 stats
75 ->Array.mapWithIndex((elem, i) =>
76 <div className="mb-2">
77 <h3 className="font-bold text-[var(--color-blue)] mb-0">
78 {("Leg " ++ (i + 1)->Int.toString)->Preact.string}
79 </h3>
80 elem
81 </div>
82 )
83 ->Preact.array
84 }}
85 </div>
86 </Maplibre.Popup>,
87 ),
88 )
89 } else {
90 Log.warn("[MapInstance] All feature stats failed to load")
91 }
92 }
93 )()
94}
95
96let handleDistance = (
97 ~event: Maplibre.mapLayerMouseEvent,
98 ~popup: Preact.Signal.t<option<Jsx.element>>,
99 ~waypoints: array<Waypoint.attributes>,
100) => {
101 Log.debug("[MapInstance] Mouse enter event")
102 switch event.features {
103 | Some(features) =>
104 // Clear existing popup first to prevent stale data
105 popup->Preact.Signal.set(None)
106 // Fetch stats for all features concurrently
107 getStats(features, event, waypoints, popup)
108 | None =>
109 Log.debug("[MapInstance] No features on mouse enter")
110 popup->Preact.Signal.set(None)
111 }
112}