Browse Source

Merge pull request #211 from fifieldt/traceroute

Add Traceroute Feature
pull/260/head
Hunter Thornsberry 2 years ago
committed by GitHub
parent
commit
4653656420
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 64
      src/components/PageComponents/Messages/ChannelChat.tsx
  2. 32
      src/components/PageComponents/Messages/TraceRoute.tsx
  3. 29
      src/core/stores/deviceStore.ts
  4. 6
      src/core/subscriptions.ts
  5. 29
      src/pages/Messages.tsx

64
src/components/PageComponents/Messages/ChannelChat.tsx

@ -5,42 +5,68 @@ import {
} from "@app/core/stores/deviceStore.js"; } from "@app/core/stores/deviceStore.js";
import { Message } from "@components/PageComponents/Messages/Message.js"; import { Message } from "@components/PageComponents/Messages/Message.js";
import { MessageInput } from "@components/PageComponents/Messages/MessageInput.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"; import { InboxIcon } from "lucide-react";
export interface ChannelChatProps { export interface ChannelChatProps {
messages?: MessageWithState[]; messages?: MessageWithState[];
channel: Types.ChannelNumber; channel: Types.ChannelNumber;
to: Types.Destination; to: Types.Destination;
traceroutes?: Types.PacketMetadata<Protobuf.Mesh.RouteDiscovery>[];
} }
export const ChannelChat = ({ export const ChannelChat = ({
messages, messages,
channel, channel,
to, to,
traceroutes,
}: ChannelChatProps): JSX.Element => { }: ChannelChatProps): JSX.Element => {
const { nodes } = useDevice(); const { nodes } = useDevice();
return ( return (
<div className="flex flex-grow flex-col"> <div className="flex flex-grow flex-col">
<div className="flex flex-grow flex-col"> <div className="flex flex-grow">
{messages ? ( <div className="flex flex-grow flex-col">
messages.map((message, index) => ( {messages ? (
<Message messages.map((message, index) => (
key={message.id} <Message
message={message} key={message.id}
lastMsgSameUser={ message={message}
index === 0 ? false : messages[index - 1].from === message.from lastMsgSameUser={
} index === 0
sender={nodes.get(message.from)} ? false
/> : messages[index - 1].from === message.from
)) }
) : ( sender={nodes.get(message.from)}
<div className="m-auto"> />
<InboxIcon className="m-auto" /> ))
<Subtle>No Messages</Subtle> ) : (
</div> <div className="m-auto">
)} <InboxIcon className="m-auto" />
<Subtle>No Messages</Subtle>
</div>
)}
</div>
<div
className={`flex flex-grow flex-col border-slate-400 border-l ${traceroutes === undefined ? "hidden" : ""}`}
>
{to === "broadcast" ? null : traceroutes ? (
traceroutes.map((traceroute, index) => (
<TraceRoute
key={traceroute.id}
from={nodes.get(traceroute.from)}
to={nodes.get(traceroute.to)}
route={traceroute.data.route}
/>
))
) : (
<div className="m-auto">
<InboxIcon className="m-auto" />
<Subtle>No Traceroutes</Subtle>
</div>
)}
</div>
</div> </div>
<div className="p-3"> <div className="p-3">
<MessageInput to={to} channel={channel} /> <MessageInput to={to} channel={channel} />

32
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<number>;
}
export const TraceRoute = ({
from,
to,
route,
}: TraceRouteProps): JSX.Element => {
const { nodes } = useDevice();
return route.length === 0 ? (
<div className="ml-5 flex">
<span className="ml-4 border-l-2 border-l-backgroundPrimary pl-2 text-textPrimary">
{to?.user?.longName}{from?.user?.longName}
</span>
</div>
) : (
<div className="ml-5 flex">
<span className="ml-4 border-l-2 border-l-backgroundPrimary pl-2 text-textPrimary">
{to?.user?.longName}
{route.map((hop) => `${nodes.get(hop)?.user?.longName ?? "Unknown"}`)}
{from?.user?.longName}
</span>
</div>
);
};

29
src/core/stores/deviceStore.ts

@ -42,6 +42,10 @@ export interface Device {
direct: Map<number, MessageWithState[]>; direct: Map<number, MessageWithState[]>;
broadcast: Map<Types.ChannelNumber, MessageWithState[]>; broadcast: Map<Types.ChannelNumber, MessageWithState[]>;
}; };
traceroutes: Map<
number,
Types.PacketMetadata<Protobuf.Mesh.RouteDiscovery>[]
>;
connection?: Types.ConnectionType; connection?: Types.ConnectionType;
activePage: Page; activePage: Page;
activeNode: number; activeNode: number;
@ -75,6 +79,9 @@ export interface Device {
addPosition: (position: Types.PacketMetadata<Protobuf.Mesh.Position>) => void; addPosition: (position: Types.PacketMetadata<Protobuf.Mesh.Position>) => void;
addConnection: (connection: Types.ConnectionType) => void; addConnection: (connection: Types.ConnectionType) => void;
addMessage: (message: MessageWithState) => void; addMessage: (message: MessageWithState) => void;
addTraceRoute: (
traceroute: Types.PacketMetadata<Protobuf.Mesh.RouteDiscovery>,
) => void;
addMetadata: (from: number, metadata: Protobuf.Mesh.DeviceMetadata) => void; addMetadata: (from: number, metadata: Protobuf.Mesh.DeviceMetadata) => void;
removeNode: (nodeNum: number) => void; removeNode: (nodeNum: number) => void;
setMessageState: ( setMessageState: (
@ -122,6 +129,7 @@ export const useDeviceStore = create<DeviceState>((set, get) => ({
direct: new Map(), direct: new Map(),
broadcast: new Map(), broadcast: new Map(),
}, },
traceroutes: new Map(),
connection: undefined, connection: undefined,
activePage: "messages", activePage: "messages",
activeNode: 0, activeNode: 0,
@ -487,6 +495,7 @@ export const useDeviceStore = create<DeviceState>((set, get) => ({
}), }),
); );
}, },
addMetadata: (from, metadata) => { addMetadata: (from, metadata) => {
set( set(
produce<DeviceState>((draft) => { produce<DeviceState>((draft) => {
@ -498,6 +507,26 @@ export const useDeviceStore = create<DeviceState>((set, get) => ({
}), }),
); );
}, },
addTraceRoute: (traceroute) => {
set(
produce<DeviceState>((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) => { removeNode: (nodeNum) => {
set( set(
produce<DeviceState>((draft) => { produce<DeviceState>((draft) => {

6
src/core/subscriptions.ts

@ -86,6 +86,12 @@ export const subscribeAll = (
}); });
}); });
connection.events.onTraceRoutePacket.subscribe((traceRoutePacket) => {
device.addTraceRoute({
...traceRoutePacket,
});
});
connection.events.onPendingSettingsChange.subscribe((state) => { connection.events.onPendingSettingsChange.subscribe((state) => {
device.setPendingSettingsChanges(state); device.setPendingSettingsChanges(state);
}); });

29
src/pages/Messages.tsx

@ -3,15 +3,17 @@ import { PageLayout } from "@components/PageLayout.js";
import { Sidebar } from "@components/Sidebar.js"; import { Sidebar } from "@components/Sidebar.js";
import { SidebarSection } from "@components/UI/Sidebar/SidebarSection.js"; import { SidebarSection } from "@components/UI/Sidebar/SidebarSection.js";
import { SidebarButton } from "@components/UI/Sidebar/sidebarButton.js"; import { SidebarButton } from "@components/UI/Sidebar/sidebarButton.js";
import { useToast } from "@core/hooks/useToast.js";
import { useDevice } from "@core/stores/deviceStore.js"; import { useDevice } from "@core/stores/deviceStore.js";
import { Hashicon } from "@emeraldpay/hashicon-react"; import { Hashicon } from "@emeraldpay/hashicon-react";
import { Protobuf, Types } from "@meshtastic/js"; import { Protobuf, Types } from "@meshtastic/js";
import { getChannelName } from "@pages/Channels.js"; import { getChannelName } from "@pages/Channels.js";
import { HashIcon } from "lucide-react"; import { HashIcon, WaypointsIcon } from "lucide-react";
import { useState } from "react"; import { useState } from "react";
export const MessagesPage = (): JSX.Element => { export const MessagesPage = (): JSX.Element => {
const { channels, nodes, hardware, messages } = useDevice(); const { channels, nodes, hardware, messages, traceroutes, connection } =
useDevice();
const [chatType, setChatType] = const [chatType, setChatType] =
useState<Types.PacketDestination>("broadcast"); useState<Types.PacketDestination>("broadcast");
const [activeChat, setActiveChat] = useState<number>( const [activeChat, setActiveChat] = useState<number>(
@ -25,6 +27,7 @@ export const MessagesPage = (): JSX.Element => {
(ch) => ch.role !== Protobuf.Channel.Channel_Role.DISABLED, (ch) => ch.role !== Protobuf.Channel.Channel_Role.DISABLED,
); );
const currentChannel = channels.get(activeChat); const currentChannel = channels.get(activeChat);
const { toast } = useToast();
return ( return (
<> <>
@ -72,6 +75,27 @@ export const MessagesPage = (): JSX.Element => {
? nodes.get(activeChat)?.user?.longName ?? "Unknown" ? nodes.get(activeChat)?.user?.longName ?? "Unknown"
: "Loading..." : "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( {allChannels.map(
(channel) => (channel) =>
@ -92,6 +116,7 @@ export const MessagesPage = (): JSX.Element => {
to={activeChat} to={activeChat}
messages={messages.direct.get(node.num)} messages={messages.direct.get(node.num)}
channel={Types.ChannelNumber.Primary} channel={Types.ChannelNumber.Primary}
traceroutes={traceroutes.get(node.num)}
/> />
), ),
)} )}

Loading…
Cancel
Save