import React from 'react';
import moment from "moment";
import { formatPublicKey } from '@/lib/meshcore';
import { getNameIconLabel } from '@/lib/meshcore-map-nodeutils';
import { NodePosition } from '@/types/map';
interface NodeMarkerProps {
node: NodePosition;
showNodeNames?: boolean;
isSelected?: boolean;
isLoadingNeighbors?: boolean;
}
interface ClusterMarkerProps {
children: any[];
}
interface PopupContentProps {
node: NodePosition;
target?: '_blank' | '_self' | '_parent' | '_top';
}
// Individual node marker component
export function NodeMarker({ node, showNodeNames = true, isSelected = false, isLoadingNeighbors = false }: NodeMarkerProps) {
const getMarkerClass = () => {
let baseClass = "custom-node-marker";
if (node.type === "meshtastic") {
baseClass += " custom-node-marker--green";
} else if (node.type === "meshcore") {
baseClass += " custom-node-marker--blue custom-node-marker--top";
}
// Only add loading class when actually loading neighbors
if (isLoadingNeighbors) {
baseClass += " custom-node-marker--loading";
}
return baseClass;
};
return (
{showNodeNames && node.short_name && (
{node.type === "meshcore" ? getNameIconLabel(node.name || node.short_name) : node.short_name}
)}
);
}
// Cluster marker component with pie chart
export function ClusterMarker({ children }: ClusterMarkerProps) {
let meshtasticCount = 0;
let meshcoreCount = 0;
// Convert children to array if it's not already
const childrenArray = Array.isArray(children) ? children : [children];
childrenArray.forEach((marker: any) => {
const node = marker.options && marker.options.nodeData;
if (node?.type === 'meshtastic') meshtasticCount++;
else if (node?.type === 'meshcore') meshcoreCount++;
});
const total = meshtasticCount + meshcoreCount;
const percentMeshcore = total ? meshcoreCount / total : 0;
const percentMeshtastic = total ? meshtasticCount / total : 0;
// Pie chart SVG calculations
const r = 18;
const c = 2 * Math.PI * r;
const meshcoreArc = percentMeshcore * c;
const meshtasticArc = percentMeshtastic * c;
return (
{total}
);
}
// Popup content component
export function PopupContent({ node, target = '_self' }: PopupContentProps) {
return (
ID: {node.type === "meshcore" ? formatPublicKey(node.node_id) : node.node_id}
Full Name: {node.name ?? "-"}
Short Name: {node.type === "meshcore" && node.short_name ? getNameIconLabel(node.name || node.short_name) : (node.short_name ?? "-")}
Type: {node.type ?? "-"}
Lat: {node.latitude}
Lng: {node.longitude}
Alt: {node.altitude !== undefined ? node.altitude : "-"}
{node.last_seen ? (
Last seen: {moment.utc(node.last_seen).format('YYYY-MM-DD HH:mm:ss')} (UTC)
{moment.utc(node.last_seen).local().fromNow()}
) : (
Last seen: -
)}
{node.first_seen ? (
First seen: {moment.utc(node.first_seen).format('YYYY-MM-DD HH:mm:ss')} (UTC)
{moment.utc(node.first_seen).local().fromNow()}
) : (
First seen: -
)}
{node.type === "meshcore" && (
)}
);
}