diff --git a/src/components/Dialog/LocationResponseDialog.tsx b/src/components/Dialog/LocationResponseDialog.tsx new file mode 100644 index 00000000..825046e6 --- /dev/null +++ b/src/components/Dialog/LocationResponseDialog.tsx @@ -0,0 +1,62 @@ +import { useDevice } from "@app/core/stores/deviceStore"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, +} from "@components/UI/Dialog"; +import type { Protobuf, Types } from "@meshtastic/js"; +import { numberToHexUnpadded } from "@noble/curves/abstract/utils"; +import type { JSX } from "react"; + +export interface LocationResponseDialogProps { + location: Types.PacketMetadata | undefined; + open: boolean; + onOpenChange: () => void; +} + +export const LocationResponseDialog = ({ + location, + open, + onOpenChange, +}: LocationResponseDialogProps): JSX.Element => { + const { nodes } = useDevice(); + + const from = nodes.get(location?.from ?? 0); + const longName = + from?.user?.longName ?? + (from ? `!${numberToHexUnpadded(from?.num)}` : "Unknown"); + const shortName = + from?.user?.shortName ?? + (from ? `${numberToHexUnpadded(from?.num).substring(0, 4)}` : "UNK"); + + return ( + + + + {`Location: ${longName} (${shortName})`} + + +
+ +

+ Coordinates:{" "} + + {location?.data.latitudeI / 1e7},{" "} + {location?.data.longitudeI / 1e7} + +

+

Altitude: {location?.data.altitude}m

+
+
+
+
+
+ ); +}; diff --git a/src/pages/Nodes.tsx b/src/pages/Nodes.tsx index a47877ab..fa1c5788 100644 --- a/src/pages/Nodes.tsx +++ b/src/pages/Nodes.tsx @@ -1,3 +1,4 @@ +import { LocationResponseDialog } from "@app/components/Dialog/LocationResponseDialog"; import { NodeOptionsDialog } from "@app/components/Dialog/NodeOptionsDialog"; import { TracerouteResponseDialog } from "@app/components/Dialog/TracerouteResponseDialog"; import Footer from "@app/components/UI/Footer"; @@ -25,6 +26,9 @@ const NodesPage = (): JSX.Element => { const [selectedTraceroute, setSelectedTraceroute] = useState< Types.PacketMetadata | undefined >(); + const [selectedLocation, setSelectedLocation] = useState< + Types.PacketMetadata | undefined + >(); const [searchTerm, setSearchTerm] = useState(""); const filteredNodes = Array.from(nodes.values()).filter((node) => { @@ -34,9 +38,10 @@ const NodesPage = (): JSX.Element => { }); useEffect(() => { - connection?.events.onTraceRoutePacket.subscribe(handleTraceroute); + if (!connection) return; + connection.events.onTraceRoutePacket.subscribe(handleTraceroute); return () => { - connection?.events.onTraceRoutePacket.unsubscribe(handleTraceroute); + connection.events.onTraceRoutePacket.unsubscribe(handleTraceroute); }; }, [connection]); @@ -47,6 +52,21 @@ const NodesPage = (): JSX.Element => { [], ); + useEffect(() => { + if (!connection) return; + connection.events.onPositionPacket.subscribe(handleLocation); + return () => { + connection.events.onPositionPacket.subscribe(handleLocation); + }; + }, [connection]); + + const handleLocation = useCallback( + (location: Types.PacketMetadata) => { + setSelectedLocation(location); + }, + [], + ); + return ( <> @@ -155,6 +175,11 @@ const NodesPage = (): JSX.Element => { open={!!selectedTraceroute} onOpenChange={() => setSelectedTraceroute(undefined)} /> + setSelectedLocation(undefined)} + />