diff --git a/src/components/PageComponents/Messages/ChannelChat.tsx b/src/components/PageComponents/Messages/ChannelChat.tsx index e6cfb3c1..81ed007f 100644 --- a/src/components/PageComponents/Messages/ChannelChat.tsx +++ b/src/components/PageComponents/Messages/ChannelChat.tsx @@ -5,42 +5,68 @@ import { } from "@app/core/stores/deviceStore.js"; import { Message } from "@components/PageComponents/Messages/Message.js"; import { MessageInput } from "@components/PageComponents/Messages/MessageInput.js"; -import type { Types } from "@meshtastic/js"; +import { TraceRoute } from "@components/PageComponents/Messages/TraceRoute.js"; +import type { Protobuf, Types } from "@meshtastic/js"; import { InboxIcon } from "lucide-react"; export interface ChannelChatProps { messages?: MessageWithState[]; channel: Types.ChannelNumber; to: Types.Destination; + traceroutes?: Types.PacketMetadata[]; } export const ChannelChat = ({ messages, channel, to, + traceroutes, }: ChannelChatProps): JSX.Element => { const { nodes } = useDevice(); return (
-
- {messages ? ( - messages.map((message, index) => ( - - )) - ) : ( -
- - No Messages -
- )} +
+
+ {messages ? ( + messages.map((message, index) => ( + + )) + ) : ( +
+ + No Messages +
+ )} +
+
+ {to === "broadcast" ? null : traceroutes ? ( + traceroutes.map((traceroute, index) => ( + + )) + ) : ( +
+ + No Traceroutes +
+ )} +
diff --git a/src/components/PageComponents/Messages/TraceRoute.tsx b/src/components/PageComponents/Messages/TraceRoute.tsx new file mode 100644 index 00000000..15d3bdea --- /dev/null +++ b/src/components/PageComponents/Messages/TraceRoute.tsx @@ -0,0 +1,32 @@ +import { useDevice } from "@app/core/stores/deviceStore.js"; +import type { Protobuf } from "@meshtastic/js"; + +export interface TraceRouteProps { + from?: Protobuf.Mesh.NodeInfo; + to?: Protobuf.Mesh.NodeInfo; + route: Array; +} + +export const TraceRoute = ({ + from, + to, + route, +}: TraceRouteProps): JSX.Element => { + const { nodes } = useDevice(); + + return route.length === 0 ? ( +
+ + {to?.user?.longName}↔{from?.user?.longName} + +
+ ) : ( +
+ + {to?.user?.longName}↔ + {route.map((hop) => `${nodes.get(hop)?.user?.longName ?? "Unknown"}↔`)} + {from?.user?.longName} + +
+ ); +}; diff --git a/src/core/stores/deviceStore.ts b/src/core/stores/deviceStore.ts index d88d1c56..29ac6ee0 100644 --- a/src/core/stores/deviceStore.ts +++ b/src/core/stores/deviceStore.ts @@ -42,6 +42,10 @@ export interface Device { direct: Map; broadcast: Map; }; + traceroutes: Map< + number, + Types.PacketMetadata[] + >; connection?: Types.ConnectionType; activePage: Page; activeNode: number; @@ -75,6 +79,9 @@ export interface Device { addPosition: (position: Types.PacketMetadata) => void; addConnection: (connection: Types.ConnectionType) => void; addMessage: (message: MessageWithState) => void; + addTraceRoute: ( + traceroute: Types.PacketMetadata, + ) => void; addMetadata: (from: number, metadata: Protobuf.Mesh.DeviceMetadata) => void; removeNode: (nodeNum: number) => void; setMessageState: ( @@ -122,6 +129,7 @@ export const useDeviceStore = create((set, get) => ({ direct: new Map(), broadcast: new Map(), }, + traceroutes: new Map(), connection: undefined, activePage: "messages", activeNode: 0, @@ -487,6 +495,7 @@ export const useDeviceStore = create((set, get) => ({ }), ); }, + addMetadata: (from, metadata) => { set( produce((draft) => { @@ -498,6 +507,26 @@ export const useDeviceStore = create((set, get) => ({ }), ); }, + addTraceRoute: (traceroute) => { + set( + produce((draft) => { + console.log("addTraceRoute called"); + console.log(traceroute); + const device = draft.devices.get(id); + if (!device) { + return; + } + + const nodetraceroutes = device.traceroutes.get(traceroute.from); + if (nodetraceroutes) { + nodetraceroutes.push(traceroute); + device.traceroutes.set(traceroute.from, nodetraceroutes); + } else { + device.traceroutes.set(traceroute.from, [traceroute]); + } + }), + ); + }, removeNode: (nodeNum) => { set( produce((draft) => { diff --git a/src/core/subscriptions.ts b/src/core/subscriptions.ts index cbc58b1e..36bc72d2 100644 --- a/src/core/subscriptions.ts +++ b/src/core/subscriptions.ts @@ -86,6 +86,12 @@ export const subscribeAll = ( }); }); + connection.events.onTraceRoutePacket.subscribe((traceRoutePacket) => { + device.addTraceRoute({ + ...traceRoutePacket, + }); + }); + connection.events.onPendingSettingsChange.subscribe((state) => { device.setPendingSettingsChanges(state); }); diff --git a/src/pages/Messages.tsx b/src/pages/Messages.tsx index 82325107..4afe1e62 100644 --- a/src/pages/Messages.tsx +++ b/src/pages/Messages.tsx @@ -3,15 +3,17 @@ import { PageLayout } from "@components/PageLayout.js"; import { Sidebar } from "@components/Sidebar.js"; import { SidebarSection } from "@components/UI/Sidebar/SidebarSection.js"; import { SidebarButton } from "@components/UI/Sidebar/sidebarButton.js"; +import { useToast } from "@core/hooks/useToast.js"; import { useDevice } from "@core/stores/deviceStore.js"; import { Hashicon } from "@emeraldpay/hashicon-react"; import { Protobuf, Types } from "@meshtastic/js"; import { getChannelName } from "@pages/Channels.js"; -import { HashIcon } from "lucide-react"; +import { HashIcon, WaypointsIcon } from "lucide-react"; import { useState } from "react"; export const MessagesPage = (): JSX.Element => { - const { channels, nodes, hardware, messages } = useDevice(); + const { channels, nodes, hardware, messages, traceroutes, connection } = + useDevice(); const [chatType, setChatType] = useState("broadcast"); const [activeChat, setActiveChat] = useState( @@ -25,6 +27,7 @@ export const MessagesPage = (): JSX.Element => { (ch) => ch.role !== Protobuf.Channel.Channel_Role.DISABLED, ); const currentChannel = channels.get(activeChat); + const { toast } = useToast(); return ( <> @@ -72,6 +75,27 @@ export const MessagesPage = (): JSX.Element => { ? nodes.get(activeChat)?.user?.longName ?? "Unknown" : "Loading..." }`} + actions={ + chatType === "direct" + ? [ + { + icon: WaypointsIcon, + async onClick() { + const targetNode = nodes.get(activeChat)?.num; + if (targetNode === undefined) return; + toast({ + title: "Sending Traceroute, please wait...", + }); + await connection?.traceRoute(targetNode).then(() => + toast({ + title: "Traceroute sent.", + }), + ); + }, + }, + ] + : [] + } > {allChannels.map( (channel) => @@ -92,6 +116,7 @@ export const MessagesPage = (): JSX.Element => { to={activeChat} messages={messages.direct.get(node.num)} channel={Types.ChannelNumber.Primary} + traceroutes={traceroutes.get(node.num)} /> ), )}