From efe2d7c77f96ea0c42a83e52387551d2eafc7a4c Mon Sep 17 00:00:00 2001 From: Dan Ditomaso Date: Tue, 10 Jun 2025 10:47:03 -0400 Subject: [PATCH] fixed tsc errors --- src/pages/Nodes/index.tsx | 288 ++++++++++++++++++++++++++++++-------- 1 file changed, 227 insertions(+), 61 deletions(-) diff --git a/src/pages/Nodes/index.tsx b/src/pages/Nodes/index.tsx index f87a8645..c349cde3 100644 --- a/src/pages/Nodes/index.tsx +++ b/src/pages/Nodes/index.tsx @@ -1,86 +1,252 @@ -// @/pages/NodesPage.tsx (abbreviated) -import { useVirtualizer } from "@tanstack/react-virtual"; -import { JSX, useCallback, useDeferredValue, useRef, useState } from "react"; -import { NodeRow } from "./NodeRow.tsx"; +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 { - FilterState, + 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 { PageLayout } from "@components/PageLayout.tsx"; -import { Sidebar } from "@components/Sidebar.tsx"; -import { useAppStore } from "@core/stores/appStore.ts"; -import { useDevice } from "@core/stores/deviceStore.ts"; -// ... other imports +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 { getNodes, hasNodeError, setDialogOpen } = useDevice(); + const { t } = useTranslation("nodes"); + const { getNodes, hardware, connection, hasNodeError, setDialogOpen } = + useDevice(); const { setNodeNumDetails } = useAppStore(); - const { nodeFilter, defaultFilterValues } = useFilterNode(); + const { nodeFilter, defaultFilterValues, isFilterDirty } = useFilterNode(); + + const [selectedTraceroute, setSelectedTraceroute] = useState< + Types.PacketMetadata | undefined + >(); + const [selectedLocation, setSelectedLocation] = useState< + Types.PacketMetadata | undefined + >(); - // No changes to filtering logic, `useDeferredValue` is still a great choice const [filterState, setFilterState] = useState(() => defaultFilterValues ); const deferredFilterState = useDeferredValue(filterState); - const filteredNodes = getNodes((node) => - nodeFilter(node, deferredFilterState) + + const filteredNodes = useMemo( + () => getNodes((node) => nodeFilter(node, deferredFilterState)), + [deferredFilterState, getNodes, nodeFilter], ); - const parentRef = useRef(null); + useEffect(() => { + if (!connection) return; + connection.events.onTraceRoutePacket.subscribe(handleTraceroute); + return () => { + connection.events.onTraceRoutePacket.unsubscribe(handleTraceroute); + }; + }, [connection]); - const rowVirtualizer = useVirtualizer({ - count: filteredNodes.length, - getScrollElement: () => parentRef.current, - estimateSize: () => 64, // Estimate row height in pixels - overscan: 5, // Render 5 extra items in each direction - }); + const handleTraceroute = useCallback( + (traceroute: Types.PacketMetadata) => { + setSelectedTraceroute(traceroute); + }, + [], + ); - const handleNodeInfoDialog = useCallback( - (nodeNum: number): void => { - setNodeNumDetails(nodeNum); - setDialogOpen("nodeDetails", true); + 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); }, - [setNodeNumDetails, setDialogOpen], + [hardware.myNodeNum], ); + function handleNodeInfoDialog(nodeNum: number): void { + setNodeNumDetails(nodeNum); + setDialogOpen("nodeDetails", true); + } + return ( <> - }> - {/* ... Filter controls ... */} - - {/* Your scrollable container with the ref */} -
- {/* Adjust height as needed */} - {/* A container to set the total size of the virtual list */} -
- {/* Map over the virtual items, not the full filteredNodes array */} - {rowVirtualizer.getVirtualItems().map((virtualItem) => { - const node = filteredNodes[virtualItem.index]; - return ( - - ); - })} + } + > +
+
+ + setFilterState((prev) => ({ + ...prev, + nodeName: e.target.value, + }))} + /> +
+
+
- {/* ... Dialogs ... */} +
+ [ +
+ +
, +

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)} + /> + );