diff --git a/src/components/map/MapView.tsx b/src/components/map/MapView.tsx index 5e925b9..f16c4ce 100644 --- a/src/components/map/MapView.tsx +++ b/src/components/map/MapView.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useEffect, useRef, useState } from 'react'; +import { useEffect, useRef, useState, useMemo } from 'react'; import maplibregl from 'maplibre-gl'; import 'maplibre-gl/dist/maplibre-gl.css'; import type { Participant, MapViewport, Waypoint, RouteSegment } from '@/types'; @@ -83,6 +83,18 @@ export default function MapView({ const [mapLoaded, setMapLoaded] = useState(false); const hasCenteredOnUserRef = useRef(false); + // Deduplicate participants by name, keeping the most recently seen one + const dedupedParticipants = useMemo(() => { + const byName = new Map(); + for (const p of participants) { + const existing = byName.get(p.name); + if (!existing || p.lastSeen > existing.lastSeen) { + byName.set(p.name, p); + } + } + return Array.from(byName.values()); + }, [participants]); + // Initialize map useEffect(() => { if (!mapContainer.current || map.current) return; @@ -160,13 +172,13 @@ export default function MapView({ useEffect(() => { if (!map.current || !mapLoaded) return; - console.log('MapView: Updating markers for', participants.length, 'participants'); - participants.forEach((p) => { + console.log('MapView: Updating markers for', dedupedParticipants.length, 'participants'); + dedupedParticipants.forEach((p) => { console.log(' -', p.name, p.id, 'location:', p.location ? `${p.location.latitude}, ${p.location.longitude}` : 'none'); }); const currentMarkers = markersRef.current; - const participantIds = new Set(participants.map((p) => p.id)); + const participantIds = new Set(dedupedParticipants.map((p) => p.id)); // Remove markers for participants who left currentMarkers.forEach((marker, id) => { @@ -177,7 +189,7 @@ export default function MapView({ }); // Add/update markers for current participants - participants.forEach((participant) => { + dedupedParticipants.forEach((participant) => { if (!participant.location) return; const { latitude, longitude } = participant.location; @@ -221,7 +233,7 @@ export default function MapView({ // Auto-center on current user's first location if (autoCenterOnUser && !hasCenteredOnUserRef.current && currentUserId) { - const currentUser = participants.find(p => p.id === currentUserId); + const currentUser = dedupedParticipants.find(p => p.id === currentUserId); if (currentUser?.location && map.current) { const { latitude, longitude } = currentUser.location; if (isValidCoordinate(latitude, longitude)) { @@ -234,7 +246,7 @@ export default function MapView({ } } } - }, [participants, mapLoaded, currentUserId, onParticipantClick, autoCenterOnUser]); + }, [dedupedParticipants, mapLoaded, currentUserId, onParticipantClick, autoCenterOnUser]); // Fly to location when zoomToLocation changes useEffect(() => { @@ -348,9 +360,9 @@ export default function MapView({ // Fit bounds to show all participants const fitToParticipants = () => { - if (!map.current || participants.length === 0) return; + if (!map.current || dedupedParticipants.length === 0) return; - const locatedParticipants = participants.filter( + const locatedParticipants = dedupedParticipants.filter( (p) => p.location && isValidCoordinate(p.location.latitude, p.location.longitude) ); if (locatedParticipants.length === 0) return; @@ -393,7 +405,7 @@ export default function MapView({ )} {/* Fit all button */} - {participants.some((p) => p.location) && ( + {dedupedParticipants.some((p) => p.location) && (