From 37a53b747cd697680e5032dcdcde96ca01113158 Mon Sep 17 00:00:00 2001 From: philon- <8975765+philon-@users.noreply.github.com> Date: Tue, 27 May 2025 13:14:57 +0200 Subject: [PATCH] Fix NodeInfoDialog initiation (#626) --- .../NodeDetailsDialog.test.tsx | 67 +++++++++++-------- .../NodeDetailsDialog/NodeDetailsDialog.tsx | 19 +++--- src/pages/Nodes.tsx | 24 +++---- 3 files changed, 63 insertions(+), 47 deletions(-) diff --git a/src/components/Dialog/NodeDetailsDialog/NodeDetailsDialog.test.tsx b/src/components/Dialog/NodeDetailsDialog/NodeDetailsDialog.test.tsx index ebd33615..8b5e3760 100644 --- a/src/components/Dialog/NodeDetailsDialog/NodeDetailsDialog.test.tsx +++ b/src/components/Dialog/NodeDetailsDialog/NodeDetailsDialog.test.tsx @@ -1,18 +1,14 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; import { render, screen } from "@testing-library/react"; import { NodeDetailsDialog } from "@components/Dialog/NodeDetailsDialog/NodeDetailsDialog.tsx"; +import { useDevice } from "@core/stores/deviceStore.ts"; import { useAppStore } from "@core/stores/appStore.ts"; import { Protobuf } from "@meshtastic/core"; -vi.mock("@core/stores/deviceStore", () => { - return { - useDevice: () => ({ - setDialogOpen: vi.fn(), - }), - }; -}); +vi.mock("@core/stores/deviceStore"); vi.mock("@core/stores/appStore"); +const mockUseDevice = vi.mocked(useDevice); const mockUseAppStore = vi.mocked(useAppStore); describe("NodeDetailsDialog", () => { @@ -42,13 +38,22 @@ describe("NodeDetailsDialog", () => { beforeEach(() => { vi.resetAllMocks(); + mockUseDevice.mockReturnValue({ + getNode: (nodeNum: number) => { + if (nodeNum === 1234) { + return mockNode; + } + return undefined; + }, + }); + mockUseAppStore.mockReturnValue({ nodeNumDetails: 1234, }); }); it("renders node details correctly", () => { - render( {}} />); + render( {}} />); expect(screen.getByText(/Node Details for Test Node \(TN\)/i)) .toBeInTheDocument(); @@ -78,10 +83,26 @@ describe("NodeDetailsDialog", () => { }); it("renders null if node is undefined", () => { - const mockNode = undefined; + const requestedNodeNum = 5678; + + mockUseAppStore.mockReturnValue({ + nodeNumDetails: requestedNodeNum, + }); + + mockUseDevice.mockReturnValue({ + getNode: (nodeNum: number) => { + if (nodeNum === requestedNodeNum) { + return undefined; + } + if (nodeNum === 1234) { + return mockNode; + } + return undefined; + }, + }); const { container } = render( - {}} />, + {}} />, ); expect(container.firstChild).toBeNull(); @@ -90,14 +111,10 @@ describe("NodeDetailsDialog", () => { it("renders correctly when position is missing", () => { const nodeWithoutPosition = { ...mockNode, position: undefined }; + mockUseDevice.mockReturnValue({ getNode: () => nodeWithoutPosition }); + mockUseAppStore.mockReturnValue({ nodeNumDetails: 1234 }); - render( - {}} - />, - ); + render( {}} />); expect(screen.queryByText(/Coordinates:/i)).not.toBeInTheDocument(); expect(screen.queryByText(/Altitude:/i)).not.toBeInTheDocument(); @@ -106,14 +123,10 @@ describe("NodeDetailsDialog", () => { it("renders correctly when deviceMetrics are missing", () => { const nodeWithoutMetrics = { ...mockNode, deviceMetrics: undefined }; + mockUseDevice.mockReturnValue({ getNode: () => nodeWithoutMetrics }); + mockUseAppStore.mockReturnValue({ nodeNumDetails: 1234 }); - render( - {}} - />, - ); + render( {}} />); expect(screen.queryByText(/Device Metrics:/i)).not.toBeInTheDocument(); expect(screen.queryByText(/Air TX utilization:/i)).not.toBeInTheDocument(); @@ -122,10 +135,10 @@ describe("NodeDetailsDialog", () => { it("renders 'Never' for lastHeard when timestamp is 0", () => { const nodeNeverHeard = { ...mockNode, lastHeard: 0 }; + mockUseDevice.mockReturnValue({ getNode: () => nodeNeverHeard }); + mockUseAppStore.mockReturnValue({ nodeNumDetails: 1234 }); - render( - {}} />, - ); + render( {}} />); expect(screen.getByText(/Last Heard: Never/i)).toBeInTheDocument(); }); diff --git a/src/components/Dialog/NodeDetailsDialog/NodeDetailsDialog.tsx b/src/components/Dialog/NodeDetailsDialog/NodeDetailsDialog.tsx index 1ac7296a..c3610de5 100644 --- a/src/components/Dialog/NodeDetailsDialog/NodeDetailsDialog.tsx +++ b/src/components/Dialog/NodeDetailsDialog/NodeDetailsDialog.tsx @@ -50,25 +50,28 @@ import { 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 { setDialogOpen, connection, setActivePage, getNode } = useDevice(); + const { setNodeNumToBeRemoved, nodeNumDetails } = useAppStore(); const { setChatType, setActiveChat } = useMessageStore(); - const { updateFavorite } = useFavoriteNode(); - const [isFavoriteState, setIsFavoriteState] = useState(false); - const { updateIgnored } = useIgnoreNode(); - const [isIgnoredState, setIsIgnoredState] = useState(false); + + const node = getNode(nodeNumDetails); + + const [isFavoriteState, setIsFavoriteState] = useState( + node?.isFavorite ?? false, + ); + const [isIgnoredState, setIsIgnoredState] = useState( + node?.isIgnored ?? false, + ); useEffect(() => { if (!node) return; diff --git a/src/pages/Nodes.tsx b/src/pages/Nodes.tsx index df4bf91c..ff398f1f 100644 --- a/src/pages/Nodes.tsx +++ b/src/pages/Nodes.tsx @@ -1,5 +1,4 @@ import { LocationResponseDialog } from "@app/components/Dialog/LocationResponseDialog.tsx"; -import { NodeDetailsDialog } from "@app/components/Dialog/NodeDetailsDialog/NodeDetailsDialog.tsx"; import { TracerouteResponseDialog } from "@app/components/Dialog/TracerouteResponseDialog.tsx"; import { Sidebar } from "@components/Sidebar.tsx"; import { Avatar } from "@components/UI/Avatar.tsx"; @@ -7,6 +6,7 @@ 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"; @@ -33,11 +33,11 @@ export interface DeleteNoteDialogProps { } const NodesPage = (): JSX.Element => { - const { getNodes, hardware, connection, hasNodeError } = useDevice(); + const { getNodes, hardware, connection, hasNodeError, setDialogOpen } = + useDevice(); + const { setNodeNumDetails } = useAppStore(); const { nodeFilter, defaultFilterValues, isFilterDirty } = useFilterNode(); - const [selectedNode, setSelectedNode] = useState< - Protobuf.Mesh.NodeInfo | undefined - >(undefined); + const [selectedTraceroute, setSelectedTraceroute] = useState< Types.PacketMetadata | undefined >(); @@ -86,6 +86,11 @@ const NodesPage = (): JSX.Element => { [hardware.myNodeNum], ); + function handleNodeInfoDialog(nodeNum: number): void { + setNodeNumDetails(nodeNum); + setDialogOpen("nodeDetails", true); + } + return ( <> { ,

setSelectedNode(node)} + onMouseDown={() => handleNodeInfoDialog(node.num)} onKeyUp={(evt) => { - evt.key === "Enter" && setSelectedNode(node); + evt.key === "Enter" && handleNodeInfoDialog(node.num); }} className="cursor-pointer underline ml-2 whitespace-break-spaces" tabIndex={0} @@ -192,11 +197,6 @@ const NodesPage = (): JSX.Element => { , ])} /> - setSelectedNode(undefined)} - />