import { LocationResponseDialog } from "@app/components/Dialog/LocationResponseDialog.tsx"; import { TracerouteResponseDialog } from "@app/components/Dialog/TracerouteResponseDialog.tsx"; import { Sidebar } from "@components/Sidebar.tsx"; import { Avatar } from "@components/UI/Avatar.tsx"; import { Mono } from "@components/generic/Mono.tsx"; import { Table } from "@components/generic/Table/index.tsx"; import { TimeAgo } from "@components/generic/TimeAgo.tsx"; import { useDevice } from "@core/stores/deviceStore.ts"; import { useAppStore } from "@core/stores/appStore.ts"; import { Protobuf, type Types } from "@meshtastic/core"; import { numberToHexUnpadded } from "@noble/curves/abstract/utils"; import { LockIcon, LockOpenIcon } from "lucide-react"; import { type JSX, useCallback, useDeferredValue, useEffect, useMemo, useState, } from "react"; import { base16 } from "rfc4648"; import { Input } from "@components/UI/Input.tsx"; import { PageLayout } from "@components/PageLayout.tsx"; import { type FilterState, useFilterNode, } from "@components/generic/Filter/useFilterNode.ts"; import { FilterControl } from "@components/generic/Filter/FilterControl.tsx"; import { useTranslation } from "react-i18next"; export interface DeleteNoteDialogProps { open: boolean; onOpenChange: (open: boolean) => void; } const NodesPage = (): JSX.Element => { const { t } = useTranslation("nodes"); const { getNodes, hardware, connection, hasNodeError, setDialogOpen } = useDevice(); const { setNodeNumDetails } = useAppStore(); const { nodeFilter, defaultFilterValues, isFilterDirty } = useFilterNode(); const [selectedTraceroute, setSelectedTraceroute] = useState< Types.PacketMetadata | undefined >(); const [selectedLocation, setSelectedLocation] = useState< Types.PacketMetadata | undefined >(); const [filterState, setFilterState] = useState(() => defaultFilterValues ); const deferredFilterState = useDeferredValue(filterState); const filteredNodes = useMemo( () => getNodes((node) => nodeFilter(node, deferredFilterState)), [deferredFilterState, getNodes, nodeFilter], ); useEffect(() => { if (!connection) return; connection.events.onTraceRoutePacket.subscribe(handleTraceroute); return () => { connection.events.onTraceRoutePacket.unsubscribe(handleTraceroute); }; }, [connection]); const handleTraceroute = useCallback( (traceroute: Types.PacketMetadata) => { setSelectedTraceroute(traceroute); }, [], ); useEffect(() => { if (!connection) return; connection.events.onPositionPacket.subscribe(handleLocation); return () => { connection.events.onPositionPacket.subscribe(handleLocation); }; }, [connection]); const handleLocation = useCallback( (location: Types.PacketMetadata) => { if (location.to.valueOf() !== hardware.myNodeNum) return; setSelectedLocation(location); }, [hardware.myNodeNum], ); function handleNodeInfoDialog(nodeNum: number): void { setNodeNumDetails(nodeNum); setDialogOpen("nodeDetails", true); } return ( <> } >
setFilterState((prev) => ({ ...prev, nodeName: e.target.value, }))} />
[
,

handleNodeInfoDialog(node.num)} onKeyUp={(evt) => { evt.key === "Enter" && handleNodeInfoDialog(node.num); }} className="cursor-pointer underline ml-2 whitespace-break-spaces" tabIndex={0} role="button" > {node.user?.longName ?? numberToHexUnpadded(node.num)}

, {node.hopsAway !== undefined ? node?.viaMqtt === false && node.hopsAway === 0 ? t("nodesTable.connectionStatus.direct") : `${node.hopsAway?.toString()} ${ node.hopsAway ?? 0 > 1 ? t("unit.hop.plural") : t("unit.hops_one") } ${t("nodesTable.connectionStatus.away")}` : t("nodesTable.connectionStatus.unknown")} {node?.viaMqtt === true ? t("nodesTable.connectionStatus.viaMqtt") : ""} , {node.lastHeard === 0 ?

{t("nodesTable.lastHeardStatus.never")}

: }
, {node.user?.publicKey && node.user?.publicKey.length > 0 ? : } , {node.snr} {t("unit.dbm")}/ {Math.min( Math.max((node.snr + 10) * 5, 0), 100, )}%/{/* Percentage */} {(node.snr + 10) * 5} {t("unit.raw")} , {Protobuf.Mesh.HardwareModel[node.user?.hwModel ?? 0]} , {base16 .stringify(node.user?.macaddr ?? []) .match(/.{1,2}/g) ?.join(":") ?? t("unknown.shortName")} , ])} /> setSelectedTraceroute(undefined)} /> setSelectedLocation(undefined)} /> ); }; export default NodesPage;