import { timestampDate } from "@bufbuild/protobuf/wkt"; import L, { DivIcon } from "leaflet"; import "leaflet.markercluster/dist/MarkerCluster.Default.css"; import "leaflet.markercluster/dist/MarkerCluster.css"; import { ArrowUpRightIcon, MapPinIcon } from "lucide-react"; import { useEffect, useMemo } from "react"; import ReactDOMServer from "react-dom/server"; import { MapContainer, Marker, Popup, TileLayer, useMap } from "react-leaflet"; import MarkerClusterGroup from "react-leaflet-cluster"; import { Link } from "react-router-dom"; import Spinner from "@/components/Spinner"; import { useAuth } from "@/contexts/AuthContext"; import { useInfiniteMemos } from "@/hooks/useMemoQueries"; import { cn } from "@/lib/utils"; import { State } from "@/types/proto/api/v1/common_pb"; import { Memo } from "@/types/proto/api/v1/memo_service_pb"; import { resolveTheme } from "@/utils/theme"; interface Props { creator: string; className?: string; } const markerIcon = new DivIcon({ className: "relative border-none", html: ReactDOMServer.renderToString( , ), }); interface ClusterGroup { getChildCount(): number; } const createClusterCustomIcon = (cluster: ClusterGroup) => { return new DivIcon({ html: `${cluster.getChildCount()}`, className: "custom-marker-cluster", iconSize: L.point(32, 32, true), }); }; const extractUserIdFromName = (name: string): string => { const match = name.match(/users\/(\d+)/); return match ? match[1] : ""; }; const MapFitBounds = ({ memos }: { memos: Memo[] }) => { const map = useMap(); useEffect(() => { if (memos.length === 0) return; const validMemos = memos.filter((m) => m.location); if (validMemos.length === 0) return; const bounds = L.latLngBounds(validMemos.map((memo) => [memo.location!.latitude, memo.location!.longitude])); map.fitBounds(bounds, { padding: [50, 50] }); }, [memos, map]); return null; }; const UserMemoMap = ({ creator, className }: Props) => { const { userGeneralSetting } = useAuth(); const creatorId = useMemo(() => extractUserIdFromName(creator), [creator]); const isDark = useMemo(() => resolveTheme(userGeneralSetting?.theme || "system").includes("dark"), [userGeneralSetting?.theme]); const { data, isLoading } = useInfiniteMemos({ state: State.NORMAL, orderBy: "display_time desc", pageSize: 1000, filter: `creator_id == ${creatorId}`, }); const memosWithLocation = useMemo(() => data?.pages.flatMap((page) => page.memos).filter((memo) => memo.location) || [], [data]); if (isLoading) { return (
); } const defaultCenter = { lat: 48.8566, lng: 2.3522 }; return (
{memosWithLocation.length === 0 && (

No location data found

)} {memosWithLocation.map((memo) => (
{memo.displayTime && timestampDate(memo.displayTime).toLocaleDateString(undefined, { year: "numeric", month: "short", day: "numeric", })} View
{memo.snippet || "No content"}
))}
); }; export default UserMemoMap;