import { useEffect, useState } from "react"; import { useAppStore } from "@core/stores/appStore.ts"; import { useDevice } from "@core/stores/deviceStore.ts"; import { MessageType, useMessageStore, } from "@core/stores/messageStore/index.ts"; import { Protobuf } from "@meshtastic/core"; import { numberToHexUnpadded } from "@noble/curves/abstract/utils"; import { DeviceImage } from "@components/generic/DeviceImage.tsx"; import { TimeAgo } from "@components/generic/TimeAgo.tsx"; import { Uptime } from "@components/generic/Uptime.tsx"; import { toast } from "@core/hooks/useToast.ts"; import { useFavoriteNode } from "../../../core/hooks/useFavoriteNode.ts"; import { useIgnoreNode } from "../../../core/hooks/useIgnoreNode.ts"; import { cn } from "@core/utils/cn.ts"; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "@components/UI/Accordion.tsx"; import { Dialog, DialogClose, DialogContent, DialogFooter, DialogHeader, DialogTitle, } from "@components/UI/Dialog.tsx"; import { Button } from "@components/UI/Button.tsx"; import { BellIcon, BellOffIcon, MapPinnedIcon, MessageSquareIcon, StarIcon, TrashIcon, WaypointsIcon, } from "lucide-react"; import { Tooltip, TooltipArrow, TooltipContent, TooltipProvider, TooltipTrigger, } from "@components/UI/Tooltip.tsx"; import { Separator } from "@components/UI/Seperator.tsx"; export interface NodeDetailsDialogProps { node: Protobuf.Mesh.NodeInfo | undefined; open: boolean; onOpenChange: (open: boolean) => void; } export const NodeDetailsDialog = ({ node, open, onOpenChange, }: NodeDetailsDialogProps) => { const { setDialogOpen, connection, setActivePage } = useDevice(); const { setNodeNumToBeRemoved } = useAppStore(); const { setChatType, setActiveChat } = useMessageStore(); const { updateFavorite } = useFavoriteNode(); const [isFavoriteState, setIsFavoriteState] = useState(false); const { updateIgnored } = useIgnoreNode(); const [isIgnoredState, setIsIgnoredState] = useState(false); useEffect(() => { if (!node) return; setIsFavoriteState(node?.isFavorite); setIsIgnoredState(node?.isIgnored); }, [node]); if (!node) return; function handleDirectMessage() { if (!node) return; setChatType(MessageType.Direct); setActiveChat(node.num); setActivePage("messages"); } function handleRequestPosition() { if (!node) return; toast({ title: "Requesting position, please wait...", }); connection?.requestPosition(node.num).then(() => toast({ title: "Position request sent.", }) ); onOpenChange(false); } function handleTraceroute() { if (!node) return; toast({ title: "Sending Traceroute, please wait...", }); connection?.traceRoute(node.num).then(() => toast({ title: "Traceroute sent.", }) ); onOpenChange(false); } function handleNodeRemove() { if (!node) return; setNodeNumToBeRemoved(node?.num); setDialogOpen("nodeRemoval", true); onOpenChange(false); } function handleToggleFavorite() { if (!node) return; updateFavorite({ nodeNum: node.num, isFavorite: !isFavoriteState }); setIsFavoriteState(!isFavoriteState); } function handleToggleIgnored() { if (!node) return; updateIgnored({ nodeNum: node.num, isIgnored: !isIgnoredState }); setIsIgnoredState(!isIgnoredState); } const deviceMetricsMap = [ { key: "airUtilTx", label: "Air TX utilization", value: node.deviceMetrics?.airUtilTx, format: (val: number) => `${val.toFixed(2)}%`, }, { key: "channelUtilization", label: "Channel utilization", value: node.deviceMetrics?.channelUtilization, format: (val: number) => `${val.toFixed(2)}%`, }, { key: "batteryLevel", label: "Battery level", value: node.deviceMetrics?.batteryLevel, format: (val: number) => `${val.toFixed(2)}%`, }, { key: "voltage", label: "Voltage", value: node.deviceMetrics?.voltage, format: (val: number) => `${val.toFixed(2)}V`, }, ]; return ( Node Details for {node.user?.longName ?? "UNKNOWN"} ( {node.user?.shortName ?? "UNK"})
{isIgnoredState ? "Unignore node" : "Ignore node"} Remove node

Details:

Node Number: {node.num}

Node Hex: !{numberToHexUnpadded(node.num)}

Role: {Protobuf.Config.Config_DeviceConfig_Role[ node.user?.role ?? 0 ].replace(/_/g, " ")}

Last Heard: {node.lastHeard === 0 ? "Never" : }

Hardware:{" "} {(Protobuf.Mesh.HardwareModel[node.user?.hwModel ?? 0] ?? "Unknown") .replace(/_/g, " ")}

Position:

{node.position ? ( <> {node.position.latitudeI && node.position.longitudeI && (

Coordinates:{" "} {node.position.latitudeI / 1e7},{" "} {node.position.longitudeI / 1e7}

)} {node.position.altitude && (

Altitude: {node.position.altitude}m

)} ) :

Unknown

}
{node.deviceMetrics && (

Device Metrics:

{deviceMetricsMap.map( (metric) => metric.value !== undefined && (

{metric.label}: {metric.format(metric.value)}

), )} {node.deviceMetrics.uptimeSeconds && (

Uptime:{" "}

)}
)}

All Raw Metrics:

                      {JSON.stringify(node, null, 2)}
                    
); };