File size: 2,301 Bytes
025301c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import { useEffect, useRef } from 'react';
import L from 'leaflet';

export default function MapView({ members, groups, sos }) {
  const elRef = useRef(null);
  const mapRef = useRef(null);
  const markersRef = useRef(new Map());
  const didFit = useRef(false);

  useEffect(() => {
    const map = L.map(elRef.current, { center: [48.8566, 2.3522], zoom: 12 });
    L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
      attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
      maxZoom: 19,
    }).addTo(map);
    mapRef.current = map;
    return () => map.remove();
  }, []);

  useEffect(() => {
    const map = mapRef.current;
    if (!map) return;

    const sosMembers = new Set(sos.filter((a) => a.state !== 'resolved').map((a) => a.member_id));
    const colorOf = (gid) => groups.find((g) => g.id === gid)?.color || '#64748b';
    const positioned = members.filter((m) => m.lat != null && m.lng != null);
    const seen = new Set();

    for (const m of positioned) {
      seen.add(m.id);
      const isSos = sosMembers.has(m.id);
      const icon = L.divIcon({
        className: 'marker-wrap',
        html: `<div class="marker ${m.status} ${isSos ? 'sos' : ''}" style="--c:${colorOf(m.group_id)}"></div>`,
        iconSize: [16, 16],
        iconAnchor: [8, 8],
      });
      let mk = markersRef.current.get(m.id);
      if (!mk) {
        mk = L.marker([m.lat, m.lng], { icon }).addTo(map);
        markersRef.current.set(m.id, mk);
      } else {
        mk.setLatLng([m.lat, m.lng]);
        mk.setIcon(icon);
      }
      mk.bindPopup(
        `<b>${m.name}</b>${m.callsign ? ` · ${m.callsign}` : ''}<br/>` +
        `${m.group_name || 'no group'} · ${m.status}<br/>` +
        `battery ${m.battery != null ? m.battery + '%' : '–'}<br/>` +
        `${m.lat.toFixed(5)}, ${m.lng.toFixed(5)}`
      );
    }
    for (const [id, mk] of markersRef.current) {
      if (!seen.has(id)) {
        map.removeLayer(mk);
        markersRef.current.delete(id);
      }
    }
    if (!didFit.current && positioned.length) {
      didFit.current = true;
      map.fitBounds(L.latLngBounds(positioned.map((m) => [m.lat, m.lng])).pad(0.3));
    }
  }, [members, groups, sos]);

  return <div ref={elRef} className="map" />;
}