import React, {useEffect, useState} from "react"; import {Link, useNavigate} from "react-router"; import {CardContent, CardDescription, CardGroup, CardHeader, CardMeta} from "semantic-ui-react"; import {getMapSearchStatus, searchMap} from "../api"; import {Card, Header, Loader, Segment} from "./Theme"; import {Paginator} from "./Common"; import {useLatestRequest, usePages} from "../hooks/customHooks"; import {QueryContext, ThemeContext} from "../contexts/contexts"; import {MAP_VIEWER_URI} from "./Vars"; function formatPopulation(pop) { if (!pop) return null; if (pop >= 1_000_000) return `${(pop / 1_000_000).toFixed(1)}M`; if (pop >= 1_000) return `${Math.round(pop / 1_000)}K`; return `${pop}`; } function MapPlaceCard({place, index}) { const navigate = useNavigate(); const {s} = React.useContext(ThemeContext); const [iframeSrc, setIframeSrc] = useState(null); const zoom = place.min_zoom ? Math.max(8, Math.round(13 - place.min_zoom / 3)) : 11; const embedSrc = `${MAP_VIEWER_URI}/embed.html#${place.lat},${place.lon},${zoom}`; // Stagger iframe loading to avoid overwhelming the map viewer with simultaneous requests. useEffect(() => { const timer = setTimeout(() => setIframeSrc(embedSrc), 100 + index * 200); return () => clearTimeout(timer); }, [embedSrc, index]); const handleClick = () => { navigate(`/map?lat=${place.lat}&lon=${place.lon}&z=${zoom}`); }; const subtitle = []; if (place.region) subtitle.push(place.region); const detail = place.kind_detail || place.kind || ""; if (detail && !place.region) subtitle.push(detail.replace(/_/g, " ")); const pop = formatPopulation(place.population); if (pop) subtitle.push(`pop. ${pop}`); return {iframeSrc && } {place.name} {subtitle.length > 0 && {subtitle.join(" \u00b7 ")}} {place.lat.toFixed(4)}, {place.lon.toFixed(4)} ; } export function MapSearchView() { const {searchParams} = React.useContext(QueryContext); const searchStr = searchParams.get("q") || ""; const {offset, limit, activePage, setPage, totalPages, setTotal} = usePages(12); // useLatestRequest discards stale responses and catches errors, so rapid pagination // can't show out-of-order results and a network failure can't leave the spinner stuck. const {data, sendRequest, loading} = useLatestRequest(300); const [hasMaps, setHasMaps] = useState(null); useEffect(() => { let cancelled = false; (async () => { const status = await getMapSearchStatus(); if (!cancelled) { const installed = (status.indexed || []).length + (status.missing || []).length; setHasMaps(installed > 0); } })(); return () => { cancelled = true; }; }, []); useEffect(() => { if (!searchStr || searchStr.length < 2) { setTotal(0); return; } sendRequest(async () => await searchMap(searchStr, limit, offset)); }, [searchStr, offset, limit, sendRequest]); const results = (data && data.results) || []; useEffect(() => { setTotal((data && data.total) || 0); }, [data, setTotal]); if (hasMaps === false) { return No maps are installed. Subscribe to a map region to search for places. Go to Map Manager ; } if (loading) { return ; } if (!searchStr) { return Enter a search query to find map locations.; } if (results.length === 0) { return No map locations found for "{searchStr}".; } return {results.map((place, i) => )} ; }
Subscribe to a map region to search for places.