diff --git a/src/components/networking/useNetworkGraph.ts b/src/components/networking/useNetworkGraph.ts index e886bae..8e451d4 100644 --- a/src/components/networking/useNetworkGraph.ts +++ b/src/components/networking/useNetworkGraph.ts @@ -156,10 +156,36 @@ export function useNetworkGraph(options: UseNetworkGraphOptions = {}): UseNetwor // Fetch graph, optionally scoped to room let graph: NetworkGraph; - if (participantIds.length > 0) { - graph = await getRoomNetworkGraph(participantIds); - } else { - graph = await getMyNetworkGraph(); + try { + if (participantIds.length > 0) { + graph = await getRoomNetworkGraph(participantIds); + } else { + graph = await getMyNetworkGraph(); + } + } catch (apiError: any) { + // If API call fails (e.g., 401 Unauthorized), fall back to showing room participants + console.warn('Network graph API failed, falling back to room participants:', apiError.message); + const fallbackNodes: GraphNode[] = roomParticipants.map(participant => ({ + id: participant.id, + username: participant.username, + displayName: participant.username, + avatarColor: participant.color, + isInRoom: true, + roomPresenceColor: participant.color, + isCurrentUser: participant.id === session.username || participant.id === roomParticipants[0]?.id, + isAnonymous: false, + trustLevelTo: undefined, + trustLevelFrom: undefined, + })); + + setState({ + nodes: fallbackNodes, + edges: [], + myConnections: [], + isLoading: false, + error: null, // Don't show error to user - graceful degradation + }); + return; } // Enrich nodes with room status, current user flag, and anonymous status diff --git a/src/shapes/MapShapeUtil.tsx b/src/shapes/MapShapeUtil.tsx index 52b6e23..8b52183 100644 --- a/src/shapes/MapShapeUtil.tsx +++ b/src/shapes/MapShapeUtil.tsx @@ -338,6 +338,7 @@ function MapComponent({ shape, editor, isSelected }: { shape: IMapShape; editor: const [searchResults, setSearchResults] = useState([]); const [_nearbyPlaces, setNearbyPlaces] = useState([]); const [isSearching, setIsSearching] = useState(false); + const [isFetchingNearby, setIsFetchingNearby] = useState(false); const [observingUser, setObservingUser] = useState(null); const styleKey = (shape.props.styleKey || 'voyager') as StyleKey; @@ -578,12 +579,12 @@ function MapComponent({ shape, editor, isSelected }: { shape: IMapShape; editor: // Actions // ========================================================================== - const addAnnotation = useCallback((type: Annotation['type'], coordinates: Coordinate[]) => { + const addAnnotation = useCallback((type: Annotation['type'], coordinates: Coordinate[], options?: { name?: string; color?: string }) => { const newAnnotation: Annotation = { - id: `ann-${Date.now()}`, + id: `ann-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`, type, - name: `${type.charAt(0).toUpperCase() + type.slice(1)} ${shape.props.annotations.length + 1}`, - color: selectedColor, + name: options?.name || `${type.charAt(0).toUpperCase() + type.slice(1)} ${shape.props.annotations.length + 1}`, + color: options?.color || selectedColor, visible: true, coordinates, createdAt: Date.now(), @@ -680,11 +681,16 @@ function MapComponent({ shape, editor, isSelected }: { shape: IMapShape; editor: const findNearby = useCallback(async (category: typeof NEARBY_CATEGORIES[0]) => { if (!mapRef.current || !isMountedRef.current) return; + console.log('🗺️ findNearby called for category:', category.label); + setIsFetchingNearby(true); + let bounds; try { bounds = mapRef.current.getBounds(); + console.log('🗺️ Map bounds:', bounds.toString()); } catch (err) { - // Map may have been destroyed + console.error('🗺️ Error getting bounds:', err); + setIsFetchingNearby(false); return; } @@ -696,15 +702,21 @@ function MapComponent({ shape, editor, isSelected }: { shape: IMapShape; editor: ); out body 10; `; + console.log('🗺️ Overpass query:', query); const response = await fetch('https://overpass-api.de/api/interpreter', { method: 'POST', body: query, }); - if (!isMountedRef.current) return; + if (!isMountedRef.current) { + setIsFetchingNearby(false); + return; + } + console.log('🗺️ Overpass response status:', response.status); const data = await response.json() as { elements: { id: number; lat: number; lon: number; tags?: { name?: string; amenity?: string } }[] }; + console.log('🗺️ Found', data.elements.length, 'places'); const places = data.elements.slice(0, 10).map((el) => ({ id: el.id, @@ -715,17 +727,26 @@ function MapComponent({ shape, editor, isSelected }: { shape: IMapShape; editor: color: category.color, })); - if (!isMountedRef.current) return; + if (!isMountedRef.current) { + setIsFetchingNearby(false); + return; + } setNearbyPlaces(places); // Add markers for nearby places + console.log('🗺️ Adding', places.length, 'markers'); places.forEach((place: any) => { if (isMountedRef.current) { - addAnnotation('marker', [{ lat: place.lat, lng: place.lng }]); + addAnnotation('marker', [{ lat: place.lat, lng: place.lng }], { + name: place.name, + color: place.color, + }); } }); + setIsFetchingNearby(false); } catch (err) { - console.error('Find nearby error:', err); + console.error('🗺️ Find nearby error:', err); + setIsFetchingNearby(false); } }, [addAnnotation]); @@ -972,19 +993,21 @@ function MapComponent({ shape, editor, isSelected }: { shape: IMapShape; editor: {/* Find Nearby */}
-
Find nearby
-
+
+ Find nearby {isFetchingNearby && } +
+
{NEARBY_CATEGORIES.map((cat) => (
findNearby(cat)} + onClick={() => !isFetchingNearby && findNearby(cat)} onPointerDown={stopPropagation} style={{ textAlign: 'center', padding: '10px 4px', borderRadius: 6, - cursor: 'pointer', + cursor: isFetchingNearby ? 'wait' : 'pointer', fontSize: 11, color: '#626C72', }}