diff --git a/src/components/map/DualMapView.tsx b/src/components/map/DualMapView.tsx
index 205b9a1..89be594 100644
--- a/src/components/map/DualMapView.tsx
+++ b/src/components/map/DualMapView.tsx
@@ -209,38 +209,11 @@ export default function DualMapView({
)}
- {/* Indoor Map button - switch to indoor view */}
- {activeView === 'outdoor' && (
-
- )}
-
- {/* Auto-mode indicator */}
- {mode === 'auto' && (
-
- Auto-detecting location
-
- )}
{/* Venue proximity indicator */}
{currentLocation && isInC3NavArea(currentLocation.latitude, currentLocation.longitude) && activeView === 'outdoor' && (
-
+
You're at the venue!
-
)}
diff --git a/src/components/map/MapView.tsx b/src/components/map/MapView.tsx
index f16c4ce..b1b7c5a 100644
--- a/src/components/map/MapView.tsx
+++ b/src/components/map/MapView.tsx
@@ -57,6 +57,12 @@ function isValidCoordinate(lat: number, lng: number): boolean {
);
}
+// Check if a participant's location is stale (older than 5 minutes)
+const STALE_THRESHOLD_MS = 5 * 60 * 1000; // 5 minutes
+function isLocationStale(lastSeen: number): boolean {
+ return Date.now() - lastSeen > STALE_THRESHOLD_MS;
+}
+
export default function MapView({
participants,
waypoints = [],
@@ -202,15 +208,36 @@ export default function MapView({
let marker = currentMarkers.get(participant.id);
if (marker) {
- // Update existing marker position
+ // Update existing marker position and stale status
marker.setLngLat([longitude, latitude]);
+ const el = marker.getElement();
+ const isStale = isLocationStale(participant.lastSeen.getTime());
+ if (isStale) {
+ el.style.backgroundColor = '#6b7280';
+ el.style.opacity = '0.6';
+ el.title = `${participant.name} - last seen ${Math.round((Date.now() - participant.lastSeen.getTime()) / 60000)} min ago`;
+ } else {
+ el.style.backgroundColor = participant.color;
+ el.style.opacity = '1';
+ el.title = participant.name;
+ }
} else {
// Create new marker
const el = document.createElement('div');
el.className = 'friend-marker';
- el.style.backgroundColor = participant.color;
el.innerHTML = participant.emoji;
+ const isStale = isLocationStale(participant.lastSeen.getTime());
+
+ if (isStale) {
+ // Grey out stale locations
+ el.style.backgroundColor = '#6b7280'; // gray-500
+ el.style.opacity = '0.6';
+ el.title = `${participant.name} - last seen ${Math.round((Date.now() - participant.lastSeen.getTime()) / 60000)} min ago`;
+ } else {
+ el.style.backgroundColor = participant.color;
+ }
+
if (participant.id === currentUserId) {
el.classList.add('sharing');
}
diff --git a/sync-server/server.js b/sync-server/server.js
index 3112be2..cc6cd88 100644
--- a/sync-server/server.js
+++ b/sync-server/server.js
@@ -485,15 +485,34 @@ const server = createServer(async (req, res) => {
let pushSent = 0;
let pushFailed = 0;
- // First, send WebSocket message to all connected clients in the room
+ // Get room to access participant names for deduplication
+ const room = rooms.get(roomSlug);
+
+ // First, send WebSocket message to connected clients, deduplicated by name
const locationRequestMsg = JSON.stringify({ type: 'request_location' });
+ const pingedNames = new Set();
+
for (const [ws, clientInfo] of clients.entries()) {
if (clientInfo.roomSlug === roomSlug && ws.readyState === 1) {
+ // Get participant name for this client
+ const participant = room?.participants?.[clientInfo.participantId];
+ const name = participant?.name;
+
+ // Skip if we've already pinged this name
+ if (name && pingedNames.has(name)) {
+ console.log(`[${roomSlug}] Skipping duplicate ping for: ${name}`);
+ continue;
+ }
+
ws.send(locationRequestMsg);
wsSent++;
+
+ if (name) {
+ pingedNames.add(name);
+ }
}
}
- console.log(`[${roomSlug}] Location request via WebSocket: ${wsSent} clients`);
+ console.log(`[${roomSlug}] Location request via WebSocket: ${wsSent} clients (deduped by name)`);
// Then, send push notifications to offline subscribers
const subs = pushSubscriptions.get(roomSlug);