From bd48b02ef30078ea591d092c226784d581e96be8 Mon Sep 17 00:00:00 2001
From: Tilen Komel
Date: Wed, 18 Sep 2024 11:17:18 +0200
Subject: [PATCH 01/31] Updated traceroute response
---
.../PageComponents/Messages/TraceRoute.tsx | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/src/components/PageComponents/Messages/TraceRoute.tsx b/src/components/PageComponents/Messages/TraceRoute.tsx
index e2adf30e..cdc20760 100644
--- a/src/components/PageComponents/Messages/TraceRoute.tsx
+++ b/src/components/PageComponents/Messages/TraceRoute.tsx
@@ -18,17 +18,17 @@ export const TraceRoute = ({
return route.length === 0 ? (
- {to?.user?.longName}↔{from?.user?.longName}
+ {to?.user?.longName} ↔ {from?.user?.longName}
) : (
- {to?.user?.longName}↔
- {route.map((hop) => {
- const node = nodes.get(hop);
- return `${node?.user?.longName ?? (node?.num ? numberToHexUnpadded(node.num) : "Unknown")}↔`;
- })}
+ {to?.user?.longName} ↔{" "}
+ {route.map(
+ (hop) =>
+ `${nodes.get(hop)?.user?.longName ?? `!${numberToHexUnpadded(hop)}`} ↔ `,
+ )}
{from?.user?.longName}
From 93e04e1b69674086294fa5490ab83acdaf7d8eb3 Mon Sep 17 00:00:00 2001
From: Tilen Komel
Date: Wed, 18 Sep 2024 11:21:15 +0200
Subject: [PATCH 02/31] Add traceroute dialog
---
.../Dialog/TracerouteResponseDialog.tsx | 46 +++++++++++++++++++
1 file changed, 46 insertions(+)
create mode 100644 src/components/Dialog/TracerouteResponseDialog.tsx
diff --git a/src/components/Dialog/TracerouteResponseDialog.tsx b/src/components/Dialog/TracerouteResponseDialog.tsx
new file mode 100644
index 00000000..f3b1502f
--- /dev/null
+++ b/src/components/Dialog/TracerouteResponseDialog.tsx
@@ -0,0 +1,46 @@
+import { useDevice } from "@app/core/stores/deviceStore";
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogHeader,
+ DialogTitle,
+} from "@components/UI/Dialog.js";
+import type { Protobuf, Types } from "@meshtastic/js";
+import { numberToHexUnpadded } from "@noble/curves/abstract/utils";
+import { TraceRoute } from "../PageComponents/Messages/TraceRoute";
+
+export interface TracerouteResponseDialogProps {
+ traceroute: Types.PacketMetadata | undefined;
+ open: boolean;
+ onOpenChange: () => void;
+}
+
+export const TracerouteResponseDialog = ({
+ traceroute,
+ open,
+ onOpenChange,
+}: TracerouteResponseDialogProps): JSX.Element => {
+ const { nodes } = useDevice();
+ const route: number[] = traceroute?.data.route ?? [];
+ const from = nodes.get(traceroute?.from ?? 0);
+ const longName =
+ from?.user?.longName ??
+ (from ? `!${numberToHexUnpadded(from?.num)}` : "Unknown");
+ const shortName =
+ from?.user?.shortName ??
+ (from ? `${numberToHexUnpadded(from?.num).substring(0, 4)}` : "UNK");
+ const to = nodes.get(traceroute?.to ?? 0);
+ return (
+
+ );
+};
From 2a5acb877143160468af4926793a0615c62c5f84 Mon Sep 17 00:00:00 2001
From: Tilen Komel
Date: Wed, 18 Sep 2024 11:34:27 +0200
Subject: [PATCH 03/31] Add node dialog
---
src/components/Dialog/NodeOptionsDialog.tsx | 75 +++++++++++++++++++++
1 file changed, 75 insertions(+)
create mode 100644 src/components/Dialog/NodeOptionsDialog.tsx
diff --git a/src/components/Dialog/NodeOptionsDialog.tsx b/src/components/Dialog/NodeOptionsDialog.tsx
new file mode 100644
index 00000000..9269f7f9
--- /dev/null
+++ b/src/components/Dialog/NodeOptionsDialog.tsx
@@ -0,0 +1,75 @@
+import { toast } from "@app/core/hooks/useToast";
+import { useDevice } from "@app/core/stores/deviceStore";
+import {
+ Dialog,
+ DialogContent,
+ DialogHeader,
+ DialogTitle,
+} from "@components/UI/Dialog.js";
+import type { Protobuf } from "@meshtastic/js";
+import { numberToHexUnpadded } from "@noble/curves/abstract/utils";
+import { Button } from "../UI/Button";
+
+export interface NodeOptionsDialogProps {
+ node: Protobuf.Mesh.NodeInfo | undefined;
+ open: boolean;
+ onOpenChange: () => void;
+}
+
+export const NodeOptionsDialog = ({
+ node,
+ open,
+ onOpenChange,
+}: NodeOptionsDialogProps): JSX.Element => {
+ const { connection } = useDevice();
+ const longName =
+ node?.user?.longName ??
+ (node ? `!${numberToHexUnpadded(node?.num)}` : "Unknown");
+ const shortName =
+ node?.user?.shortName ??
+ (node ? `${numberToHexUnpadded(node?.num).substring(0, 4)}` : "UNK");
+
+ function handleTraceroute() {
+ if (!node) return;
+ toast({
+ title: "Sending Traceroute, please wait...",
+ });
+ connection?.traceRoute(node.num).then(() =>
+ toast({
+ title: "Traceroute sent.",
+ }),
+ );
+ onOpenChange();
+ }
+
+ function handleRequestPosition() {
+ if (!node) return;
+ toast({
+ title: "Requesting position, please wait...",
+ });
+ connection?.requestPosition(node.num).then(() =>
+ toast({
+ title: "Position request sent.",
+ }),
+ );
+ onOpenChange();
+ }
+
+ return (
+
+ );
+};
From 56ac1d55f41446f51d19ddbaefc5490ca38eccf7 Mon Sep 17 00:00:00 2001
From: Tilen Komel
Date: Wed, 18 Sep 2024 11:34:47 +0200
Subject: [PATCH 04/31] Implement on nodes page
---
src/pages/Nodes.tsx | 43 +++++++++++++++++++++++++++++++++++++++----
1 file changed, 39 insertions(+), 4 deletions(-)
diff --git a/src/pages/Nodes.tsx b/src/pages/Nodes.tsx
index 038d81a8..4b6303fd 100644
--- a/src/pages/Nodes.tsx
+++ b/src/pages/Nodes.tsx
@@ -1,3 +1,5 @@
+import { NodeOptionsDialog } from "@app/components/Dialog/NodeOptionsDialog";
+import { TracerouteResponseDialog } from "@app/components/Dialog/TracerouteResponseDialog";
import Footer from "@app/components/UI/Footer";
import { useAppStore } from "@app/core/stores/appStore";
import { Sidebar } from "@components/Sidebar.js";
@@ -7,10 +9,10 @@ import { Table } from "@components/generic/Table/index.js";
import { TimeAgo } from "@components/generic/Table/tmp/TimeAgo.js";
import { useDevice } from "@core/stores/deviceStore.js";
import { Hashicon } from "@emeraldpay/hashicon-react";
-import { Protobuf } from "@meshtastic/js";
+import { Protobuf, type Types } from "@meshtastic/js";
import { numberToHexUnpadded } from "@noble/curves/abstract/utils";
import { LockIcon, LockOpenIcon, TrashIcon } from "lucide-react";
-import { Fragment } from "react";
+import { Fragment, useCallback, useEffect, useState } from "react";
import { base16 } from "rfc4648";
export interface DeleteNoteDialogProps {
@@ -19,13 +21,36 @@ export interface DeleteNoteDialogProps {
}
export const NodesPage = (): JSX.Element => {
- const { nodes, hardware, setDialogOpen } = useDevice();
+ const { nodes, hardware, setDialogOpen, connection } = useDevice();
const { setNodeNumToBeRemoved } = useAppStore();
+ const [selectedNode, setSelectedNode] = useState<
+ Protobuf.Mesh.NodeInfo | undefined
+ >(undefined);
+ const [selectedTraceroute, setSelectedTraceroute] = useState<
+ Types.PacketMetadata | undefined
+ >();
const filteredNodes = Array.from(nodes.values()).filter(
(n) => n.num !== hardware.myNodeNum,
);
+ useEffect(() => {
+ connection?.events.onTraceRoutePacket.subscribe(handleTraceroute);
+ return () => {
+ connection?.events.onTraceRoutePacket.unsubscribe(handleTraceroute);
+ };
+ }, [
+ connection?.events.onTraceRoutePacket.subscribe,
+ connection?.events.onTraceRoutePacket.unsubscribe,
+ ]);
+
+ const handleTraceroute = useCallback(
+ (traceroute: Types.PacketMetadata) => {
+ setSelectedTraceroute(traceroute);
+ },
+ [],
+ );
+
return (
<>
@@ -45,7 +70,7 @@ export const NodesPage = (): JSX.Element => {
]}
rows={filteredNodes.map((node) => [
,
-
+ setSelectedNode(node)}>
{node.user?.longName ??
(node.user?.macaddr
? `Meshtastic ${base16
@@ -105,6 +130,16 @@ export const NodesPage = (): JSX.Element => {
,
])}
/>
+ setSelectedNode(undefined)}
+ />
+ setSelectedTraceroute(undefined)}
+ />
From a2b9a33f6a36a142f54989f7b3abc49ffe497b73 Mon Sep 17 00:00:00 2001
From: Tilen Komel
Date: Thu, 19 Sep 2024 10:37:48 +0200
Subject: [PATCH 05/31] Cursor pointer
---
src/pages/Nodes.tsx | 9 +++------
1 file changed, 3 insertions(+), 6 deletions(-)
diff --git a/src/pages/Nodes.tsx b/src/pages/Nodes.tsx
index 4b6303fd..d38a1086 100644
--- a/src/pages/Nodes.tsx
+++ b/src/pages/Nodes.tsx
@@ -39,10 +39,7 @@ export const NodesPage = (): JSX.Element => {
return () => {
connection?.events.onTraceRoutePacket.unsubscribe(handleTraceroute);
};
- }, [
- connection?.events.onTraceRoutePacket.subscribe,
- connection?.events.onTraceRoutePacket.unsubscribe,
- ]);
+ }, [connection]);
const handleTraceroute = useCallback(
(traceroute: Types.PacketMetadata) => {
@@ -70,7 +67,7 @@ export const NodesPage = (): JSX.Element => {
]}
rows={filteredNodes.map((node) => [
,
- setSelectedNode(node)}>
+ setSelectedNode(node)}>
{node.user?.longName ??
(node.user?.macaddr
? `Meshtastic ${base16
@@ -104,7 +101,7 @@ export const NodesPage = (): JSX.Element => {
{node.user?.publicKey && node.user?.publicKey.length > 0 ? (
) : (
-
+
)}
,
From aa66e1f73cd1fdbc03d10eeb000b3aca1c08abf5 Mon Sep 17 00:00:00 2001
From: Tilen Komel
Date: Thu, 23 Jan 2025 23:45:24 +0100
Subject: [PATCH 06/31] Biome
---
src/pages/Nodes.tsx | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/pages/Nodes.tsx b/src/pages/Nodes.tsx
index d38a1086..57b1a546 100644
--- a/src/pages/Nodes.tsx
+++ b/src/pages/Nodes.tsx
@@ -67,7 +67,11 @@ export const NodesPage = (): JSX.Element => {
]}
rows={filteredNodes.map((node) => [
,
- setSelectedNode(node)}>
+ setSelectedNode(node)}
+ >
{node.user?.longName ??
(node.user?.macaddr
? `Meshtastic ${base16
From f24041651cc3710416636056e8777cdf2a01dea2 Mon Sep 17 00:00:00 2001
From: Tilen Komel
Date: Fri, 24 Jan 2025 00:03:55 +0100
Subject: [PATCH 07/31] Fix legacy import
---
src/components/Dialog/NodeOptionsDialog.tsx | 2 +-
src/components/Dialog/TracerouteResponseDialog.tsx | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/components/Dialog/NodeOptionsDialog.tsx b/src/components/Dialog/NodeOptionsDialog.tsx
index 9269f7f9..f2ea85e2 100644
--- a/src/components/Dialog/NodeOptionsDialog.tsx
+++ b/src/components/Dialog/NodeOptionsDialog.tsx
@@ -5,7 +5,7 @@ import {
DialogContent,
DialogHeader,
DialogTitle,
-} from "@components/UI/Dialog.js";
+} from "@components/UI/Dialog";
import type { Protobuf } from "@meshtastic/js";
import { numberToHexUnpadded } from "@noble/curves/abstract/utils";
import { Button } from "../UI/Button";
diff --git a/src/components/Dialog/TracerouteResponseDialog.tsx b/src/components/Dialog/TracerouteResponseDialog.tsx
index f3b1502f..87ecf40e 100644
--- a/src/components/Dialog/TracerouteResponseDialog.tsx
+++ b/src/components/Dialog/TracerouteResponseDialog.tsx
@@ -5,7 +5,7 @@ import {
DialogDescription,
DialogHeader,
DialogTitle,
-} from "@components/UI/Dialog.js";
+} from "@components/UI/Dialog";
import type { Protobuf, Types } from "@meshtastic/js";
import { numberToHexUnpadded } from "@noble/curves/abstract/utils";
import { TraceRoute } from "../PageComponents/Messages/TraceRoute";
From 965e3247b0b4e5d8952f2b9168a1e00e424346b3 Mon Sep 17 00:00:00 2001
From: Tilen Komel
Date: Fri, 24 Jan 2025 00:49:27 +0100
Subject: [PATCH 08/31] Added 2way traceroute
---
.../Dialog/TracerouteResponseDialog.tsx | 3 ++-
.../PageComponents/Messages/TraceRoute.tsx | 16 +++++++++++++++-
2 files changed, 17 insertions(+), 2 deletions(-)
diff --git a/src/components/Dialog/TracerouteResponseDialog.tsx b/src/components/Dialog/TracerouteResponseDialog.tsx
index 87ecf40e..0b081d55 100644
--- a/src/components/Dialog/TracerouteResponseDialog.tsx
+++ b/src/components/Dialog/TracerouteResponseDialog.tsx
@@ -23,6 +23,7 @@ export const TracerouteResponseDialog = ({
}: TracerouteResponseDialogProps): JSX.Element => {
const { nodes } = useDevice();
const route: number[] = traceroute?.data.route ?? [];
+ const routeBack: number[] = traceroute?.data.routeBack ?? [];
const from = nodes.get(traceroute?.from ?? 0);
const longName =
from?.user?.longName ??
@@ -38,7 +39,7 @@ export const TracerouteResponseDialog = ({
{`Traceroute: ${longName} (${shortName})`}
-
+
diff --git a/src/components/PageComponents/Messages/TraceRoute.tsx b/src/components/PageComponents/Messages/TraceRoute.tsx
index 9d4c53e6..a8fa04dc 100644
--- a/src/components/PageComponents/Messages/TraceRoute.tsx
+++ b/src/components/PageComponents/Messages/TraceRoute.tsx
@@ -6,12 +6,14 @@ export interface TraceRouteProps {
from?: Protobuf.Mesh.NodeInfo;
to?: Protobuf.Mesh.NodeInfo;
route: Array;
+ routeBack?: Array;
}
export const TraceRoute = ({
from,
to,
route,
+ routeBack,
}: TraceRouteProps): JSX.Element => {
const { nodes } = useDevice();
@@ -22,8 +24,9 @@ export const TraceRoute = ({
) : (
-
+
+ Route traced towards destination:
{to?.user?.longName} ↔{" "}
{route.map(
(hop) =>
@@ -31,6 +34,17 @@ export const TraceRoute = ({
)}
{from?.user?.longName}
+ {routeBack ? (
+
+ Route traced back to us:
+ {from?.user?.longName} ↔{" "}
+ {routeBack.map(
+ (hop) =>
+ `${nodes.get(hop)?.user?.longName ?? `!${numberToHexUnpadded(hop)}`} ↔ `,
+ )}
+ {to?.user?.longName}
+
+ ) : null}
);
};
From 54c73a8c0dab346e54706142b062313b532acd8e Mon Sep 17 00:00:00 2001
From: Tilen Komel
Date: Fri, 24 Jan 2025 12:15:57 +0100
Subject: [PATCH 09/31] Add snr to traceroute
---
.../Dialog/TracerouteResponseDialog.tsx | 11 ++++-
.../PageComponents/Messages/TraceRoute.tsx | 45 +++++++++++++------
2 files changed, 41 insertions(+), 15 deletions(-)
diff --git a/src/components/Dialog/TracerouteResponseDialog.tsx b/src/components/Dialog/TracerouteResponseDialog.tsx
index 0b081d55..e26cafc2 100644
--- a/src/components/Dialog/TracerouteResponseDialog.tsx
+++ b/src/components/Dialog/TracerouteResponseDialog.tsx
@@ -24,6 +24,8 @@ export const TracerouteResponseDialog = ({
const { nodes } = useDevice();
const route: number[] = traceroute?.data.route ?? [];
const routeBack: number[] = traceroute?.data.routeBack ?? [];
+ const snrTowards = traceroute?.data.snrTowards ?? [];
+ const snrBack = traceroute?.data.snrBack ?? [];
const from = nodes.get(traceroute?.from ?? 0);
const longName =
from?.user?.longName ??
@@ -39,7 +41,14 @@ export const TracerouteResponseDialog = ({
{`Traceroute: ${longName} (${shortName})`}
-
+
diff --git a/src/components/PageComponents/Messages/TraceRoute.tsx b/src/components/PageComponents/Messages/TraceRoute.tsx
index a8fa04dc..c2dc1f5b 100644
--- a/src/components/PageComponents/Messages/TraceRoute.tsx
+++ b/src/components/PageComponents/Messages/TraceRoute.tsx
@@ -7,6 +7,8 @@ export interface TraceRouteProps {
to?: Protobuf.Mesh.NodeInfo;
route: Array;
routeBack?: Array;
+ snrTowards?: Array;
+ snrBack?: Array;
}
export const TraceRoute = ({
@@ -14,6 +16,8 @@ export const TraceRoute = ({
to,
route,
routeBack,
+ snrTowards,
+ snrBack,
}: TraceRouteProps): JSX.Element => {
const { nodes } = useDevice();
@@ -24,24 +28,37 @@ export const TraceRoute = ({
) : (
-
+
- Route traced towards destination:
- {to?.user?.longName} ↔{" "}
- {route.map(
- (hop) =>
- `${nodes.get(hop)?.user?.longName ?? `!${numberToHexUnpadded(hop)}`} ↔ `,
- )}
+ Route to destination:
+ {to?.user?.longName}
+ ↓ {snrTowards ? snrTowards[0] : null}dB
+ {route.map((hop, i) => (
+ // biome-ignore lint/suspicious/noArrayIndexKey:
+
+
+ {nodes.get(hop)?.user?.longName ?? `!${numberToHexUnpadded(hop)}`}
+
+ ↓ {snrTowards ? snrTowards[i + 1] : null}dB
+
+ ))}
{from?.user?.longName}
{routeBack ? (
-
- Route traced back to us:
- {from?.user?.longName} ↔{" "}
- {routeBack.map(
- (hop) =>
- `${nodes.get(hop)?.user?.longName ?? `!${numberToHexUnpadded(hop)}`} ↔ `,
- )}
+
+ Route back:
+ {from?.user?.longName}
+ ↓ {snrBack ? snrBack[0] : null}dB
+ {routeBack.map((hop, i) => (
+ // biome-ignore lint/suspicious/noArrayIndexKey:
+
+
+ {nodes.get(hop)?.user?.longName ??
+ `!${numberToHexUnpadded(hop)}`}
+
+ ↓ {snrBack ? snrBack[i + 1] : null}dB
+
+ ))}
{to?.user?.longName}
) : null}
From 2fbf2a1173adf9675ca2da0cf06a9afeac26d9d9 Mon Sep 17 00:00:00 2001
From: Tilen Komel
Date: Fri, 24 Jan 2025 12:26:28 +0100
Subject: [PATCH 10/31] Fix show ?? if null
---
src/components/PageComponents/Messages/TraceRoute.tsx | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/components/PageComponents/Messages/TraceRoute.tsx b/src/components/PageComponents/Messages/TraceRoute.tsx
index c2dc1f5b..ba3b9e63 100644
--- a/src/components/PageComponents/Messages/TraceRoute.tsx
+++ b/src/components/PageComponents/Messages/TraceRoute.tsx
@@ -32,14 +32,14 @@ export const TraceRoute = ({
Route to destination:
{to?.user?.longName}
- ↓ {snrTowards ? snrTowards[0] : null}dB
+ ↓ {snrTowards?.[0] ? snrTowards[0] : "??"}dB
{route.map((hop, i) => (
// biome-ignore lint/suspicious/noArrayIndexKey:
{nodes.get(hop)?.user?.longName ?? `!${numberToHexUnpadded(hop)}`}
- ↓ {snrTowards ? snrTowards[i + 1] : null}dB
+ ↓ {snrTowards?.[i + 1] ? snrTowards[i + 1] : "??"}dB
))}
{from?.user?.longName}
@@ -48,7 +48,7 @@ export const TraceRoute = ({
Route back:
{from?.user?.longName}
- ↓ {snrBack ? snrBack[0] : null}dB
+ ↓ {snrBack?.[0] ? snrBack[0] : "??"}dB
{routeBack.map((hop, i) => (
// biome-ignore lint/suspicious/noArrayIndexKey:
@@ -56,7 +56,7 @@ export const TraceRoute = ({
{nodes.get(hop)?.user?.longName ??
`!${numberToHexUnpadded(hop)}`}
- ↓ {snrBack ? snrBack[i + 1] : null}dB
+ ↓ {snrBack?.[i + 1] ? snrBack[i + 1] : "??"}dB
))}
{to?.user?.longName}
From 9eeed9630b039fb1270f1ce387375776320610f3 Mon Sep 17 00:00:00 2001
From: Tilen Komel
Date: Wed, 29 Jan 2025 13:15:52 +0100
Subject: [PATCH 11/31] Fix
---
src/components/Dialog/NodeOptionsDialog.tsx | 1 +
src/components/Dialog/TracerouteResponseDialog.tsx | 1 +
src/components/PageComponents/Messages/TraceRoute.tsx | 9 ++-------
3 files changed, 4 insertions(+), 7 deletions(-)
diff --git a/src/components/Dialog/NodeOptionsDialog.tsx b/src/components/Dialog/NodeOptionsDialog.tsx
index f2ea85e2..10a8d292 100644
--- a/src/components/Dialog/NodeOptionsDialog.tsx
+++ b/src/components/Dialog/NodeOptionsDialog.tsx
@@ -8,6 +8,7 @@ import {
} from "@components/UI/Dialog";
import type { Protobuf } from "@meshtastic/js";
import { numberToHexUnpadded } from "@noble/curves/abstract/utils";
+import type { JSX } from "react";
import { Button } from "../UI/Button";
export interface NodeOptionsDialogProps {
diff --git a/src/components/Dialog/TracerouteResponseDialog.tsx b/src/components/Dialog/TracerouteResponseDialog.tsx
index e26cafc2..9fce1cfa 100644
--- a/src/components/Dialog/TracerouteResponseDialog.tsx
+++ b/src/components/Dialog/TracerouteResponseDialog.tsx
@@ -8,6 +8,7 @@ import {
} from "@components/UI/Dialog";
import type { Protobuf, Types } from "@meshtastic/js";
import { numberToHexUnpadded } from "@noble/curves/abstract/utils";
+import type { JSX } from "react";
import { TraceRoute } from "../PageComponents/Messages/TraceRoute";
export interface TracerouteResponseDialogProps {
diff --git a/src/components/PageComponents/Messages/TraceRoute.tsx b/src/components/PageComponents/Messages/TraceRoute.tsx
index ba3b9e63..15e0dd00 100644
--- a/src/components/PageComponents/Messages/TraceRoute.tsx
+++ b/src/components/PageComponents/Messages/TraceRoute.tsx
@@ -1,6 +1,7 @@
import { useDevice } from "@app/core/stores/deviceStore.ts";
import type { Protobuf } from "@meshtastic/js";
import { numberToHexUnpadded } from "@noble/curves/abstract/utils";
+import type { JSX } from "react";
export interface TraceRouteProps {
from?: Protobuf.Mesh.NodeInfo;
@@ -21,13 +22,7 @@ export const TraceRoute = ({
}: TraceRouteProps): JSX.Element => {
const { nodes } = useDevice();
- return route.length === 0 ? (
-
-
- {to?.user?.longName} ↔ {from?.user?.longName}
-
-
- ) : (
+ return (
Route to destination:
From b3cde1bcd7a94a2bc944cdb5638bfb9ad612589c Mon Sep 17 00:00:00 2001
From: Tilen Komel
Date: Wed, 29 Jan 2025 13:56:48 +0100
Subject: [PATCH 12/31] Move remove button to quick options
---
src/components/Dialog/NodeOptionsDialog.tsx | 46 ++++++++++++++-------
src/pages/Nodes.tsx | 18 +++-----
2 files changed, 36 insertions(+), 28 deletions(-)
diff --git a/src/components/Dialog/NodeOptionsDialog.tsx b/src/components/Dialog/NodeOptionsDialog.tsx
index 10a8d292..06a3f0b8 100644
--- a/src/components/Dialog/NodeOptionsDialog.tsx
+++ b/src/components/Dialog/NodeOptionsDialog.tsx
@@ -1,4 +1,5 @@
import { toast } from "@app/core/hooks/useToast";
+import { useAppStore } from "@app/core/stores/appStore";
import { useDevice } from "@app/core/stores/deviceStore";
import {
Dialog,
@@ -8,6 +9,7 @@ import {
} from "@components/UI/Dialog";
import type { Protobuf } from "@meshtastic/js";
import { numberToHexUnpadded } from "@noble/curves/abstract/utils";
+import { TrashIcon } from "lucide-react";
import type { JSX } from "react";
import { Button } from "../UI/Button";
@@ -22,7 +24,8 @@ export const NodeOptionsDialog = ({
open,
onOpenChange,
}: NodeOptionsDialogProps): JSX.Element => {
- const { connection } = useDevice();
+ const { setDialogOpen, connection } = useDevice();
+ const { setNodeNumToBeRemoved } = useAppStore();
const longName =
node?.user?.longName ??
(node ? `!${numberToHexUnpadded(node?.num)}` : "Unknown");
@@ -30,19 +33,6 @@ export const NodeOptionsDialog = ({
node?.user?.shortName ??
(node ? `${numberToHexUnpadded(node?.num).substring(0, 4)}` : "UNK");
- function handleTraceroute() {
- if (!node) return;
- toast({
- title: "Sending Traceroute, please wait...",
- });
- connection?.traceRoute(node.num).then(() =>
- toast({
- title: "Traceroute sent.",
- }),
- );
- onOpenChange();
- }
-
function handleRequestPosition() {
if (!node) return;
toast({
@@ -56,6 +46,19 @@ export const NodeOptionsDialog = ({
onOpenChange();
}
+ function handleTraceroute() {
+ if (!node) return;
+ toast({
+ title: "Sending Traceroute, please wait...",
+ });
+ connection?.traceRoute(node.num).then(() =>
+ toast({
+ title: "Traceroute sent.",
+ }),
+ );
+ onOpenChange();
+ }
+
return (
+
+
+
diff --git a/src/components/UI/Dialog.tsx b/src/components/UI/Dialog.tsx
index 019b8142..d989b842 100644
--- a/src/components/UI/Dialog.tsx
+++ b/src/components/UI/Dialog.tsx
@@ -44,7 +44,7 @@ const DialogContent = React.forwardRef<
void;
addRasterSource: (source: RasterSource) => void;
@@ -42,6 +43,7 @@ interface AppState {
setNodeNumToBeRemoved: (nodeNum: number) => void;
setAccent: (color: AccentColor) => void;
setConnectDialogOpen: (open: boolean) => void;
+ setNodeNumDetails: (nodeNum: number) => void;
}
export const useAppStore = create()((set) => ({
@@ -57,6 +59,7 @@ export const useAppStore = create()((set) => ({
accent: "orange",
connectDialogOpen: false,
nodeNumToBeRemoved: 0,
+ nodeNumDetails: 0,
setRasterSources: (sources: RasterSource[]) => {
set(
@@ -124,4 +127,8 @@ export const useAppStore = create()((set) => ({
}),
);
},
+ setNodeNumDetails: (nodeNum) =>
+ set((state) => ({
+ nodeNumDetails: nodeNum,
+ })),
}));
diff --git a/src/core/stores/deviceStore.ts b/src/core/stores/deviceStore.ts
index bd407611..a4ad7b69 100644
--- a/src/core/stores/deviceStore.ts
+++ b/src/core/stores/deviceStore.ts
@@ -26,7 +26,8 @@ export type DialogVariant =
| "reboot"
| "deviceName"
| "nodeRemoval"
- | "pkiBackup";
+ | "pkiBackup"
+ | "nodeDetails";
export interface Device {
id: number;
@@ -62,6 +63,7 @@ export interface Device {
deviceName: boolean;
nodeRemoval: boolean;
pkiBackup: boolean;
+ nodeDetails: boolean;
};
setStatus: (status: Types.DeviceStatusEnum) => void;
@@ -145,6 +147,7 @@ export const useDeviceStore = create((set, get) => ({
deviceName: false,
nodeRemoval: false,
pkiBackup: false,
+ nodeDetails: false,
},
pendingSettingsChanges: false,
messageDraft: "",
From 4abc78fff3a48372dc40374a9258ed195a0c1e71 Mon Sep 17 00:00:00 2001
From: Tilen Komel
Date: Sun, 2 Feb 2025 12:56:07 +0100
Subject: [PATCH 18/31] Add basic accordion element
---
src/components/UI/Accordion.tsx | 44 +++++++++++++++++++++++++++++++++
1 file changed, 44 insertions(+)
create mode 100644 src/components/UI/Accordion.tsx
diff --git a/src/components/UI/Accordion.tsx b/src/components/UI/Accordion.tsx
new file mode 100644
index 00000000..a44017b1
--- /dev/null
+++ b/src/components/UI/Accordion.tsx
@@ -0,0 +1,44 @@
+import { cn } from "@core/utils/cn.ts";
+import * as AccordionPrimitive from "@radix-ui/react-accordion";
+import { ChevronDownIcon } from "lucide-react";
+import { type ComponentRef, forwardRef } from "react";
+
+export const Accordion = AccordionPrimitive.Root;
+
+export const AccordionHeader = AccordionPrimitive.Header;
+
+export const AccordionItem = AccordionPrimitive.Item;
+
+export const AccordionTrigger = forwardRef<
+ ComponentRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+ {props.children}
+
+
+));
+
+export const AccordionContent = forwardRef<
+ ComponentRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
From 0c8901b5b2c64e0728532cb76c90c33092ae098b Mon Sep 17 00:00:00 2001
From: Tilen Komel
Date: Sun, 2 Feb 2025 12:58:35 +0100
Subject: [PATCH 19/31] Make all raw data accordion in node details dialog
---
src/components/Dialog/NodeDetailsDialog.tsx | 66 +++++++--------------
1 file changed, 22 insertions(+), 44 deletions(-)
diff --git a/src/components/Dialog/NodeDetailsDialog.tsx b/src/components/Dialog/NodeDetailsDialog.tsx
index 13a9782d..8c30d0df 100644
--- a/src/components/Dialog/NodeDetailsDialog.tsx
+++ b/src/components/Dialog/NodeDetailsDialog.tsx
@@ -1,16 +1,21 @@
import { useAppStore } from "@app/core/stores/appStore";
import { useDevice } from "@app/core/stores/deviceStore";
+import {
+ Accordion,
+ AccordionContent,
+ AccordionItem,
+ AccordionTrigger,
+} from "@components/UI/Accordion";
import {
Dialog,
DialogContent,
DialogFooter,
DialogHeader,
DialogTitle,
-} from "@components/UI/Dialog.tsx";
+} from "@components/UI/Dialog";
import { Protobuf } from "@meshtastic/js";
import { numberToHexUnpadded } from "@noble/curves/abstract/utils";
-import { useEffect } from "react";
-import { useState } from "react";
+import { useEffect, useState } from "react";
import { DeviceImage } from "../generic/DeviceImage";
import { TimeAgo } from "../generic/TimeAgo";
import { Uptime } from "../generic/Uptime";
@@ -138,49 +143,22 @@ export const NodeDetailsDialog = ({
) : null}
- {device.environmentMetrics ? (
-
-
- Environment Metrics:
-
- {device.deviceMetrics.airUtilTx ? (
-
- Air TX utilization:{" "}
- {device.deviceMetrics.airUtilTx.toFixed(2)}%
-
- ) : null}
- {device.deviceMetrics.channelUtilization ? (
-
- Channel utilization:{" "}
- {device.deviceMetrics.channelUtilization.toFixed(2)}%
-
- ) : null}
- {device.deviceMetrics.batteryLevel ? (
-
- Battery level:{" "}
- {device.deviceMetrics.batteryLevel.toFixed(2)}%
-
- ) : null}
- {device.deviceMetrics.voltage ? (
-
Voltage: {device.deviceMetrics.voltage.toFixed(2)}V
- ) : null}
- {device.deviceMetrics.uptimeSeconds ? (
-
- Uptime:{" "}
-
-
- ) : null}
-
- ) : null}
-
{device ? (
-
- All Raw Metrics:
-
-
- {JSON.stringify(device, null, 2)}
-
+
+
+
+
+ All Raw Metrics:
+
+
+
+
+ {JSON.stringify(device, null, 2)}
+
+
+
+
) : null}
From 358f8a94d022e4402eb67414b4c0c641f1f51adb Mon Sep 17 00:00:00 2001
From: Tilen Komel
Date: Sun, 2 Feb 2025 14:02:47 +0100
Subject: [PATCH 20/31] Update timeAgo to use Intl.RelativeTimeFormat
---
src/components/generic/TimeAgo.tsx | 45 +++++++++++++++++++-----------
1 file changed, 29 insertions(+), 16 deletions(-)
diff --git a/src/components/generic/TimeAgo.tsx b/src/components/generic/TimeAgo.tsx
index 7507a7a3..e8d5973c 100755
--- a/src/components/generic/TimeAgo.tsx
+++ b/src/components/generic/TimeAgo.tsx
@@ -4,28 +4,41 @@ export interface TimeAgoProps {
timestamp: number;
}
-const getTimeAgo = (timestamp: number): string => {
- const now = Date.now();
- const secondsPast = Math.floor((now - timestamp) / 1000);
+const getTimeAgo = (
+ unixTimestamp: number,
+ locale: Intl.LocalesArgument = "en",
+): string => {
+ const timestamp = new Date(unixTimestamp);
+ const diff = (new Date().getTime() - timestamp.getTime()) / 1000;
- if (secondsPast < 10) return "now";
- if (secondsPast < 60) return `${secondsPast} seconds ago`;
- if (secondsPast < 3600) return `${Math.floor(secondsPast / 60)} minutes ago`;
- if (secondsPast < 86400) return `${Math.floor(secondsPast / 3600)} hours ago`;
- return `${Math.floor(secondsPast / 86400)} days ago ${secondsPast}`;
+ const minutes = Math.floor(diff / 60);
+ const hours = Math.floor(minutes / 60);
+ const days = Math.floor(hours / 24);
+ const months = Math.floor(days / 30);
+ const years = Math.floor(months / 12);
+ const rtf = new Intl.RelativeTimeFormat(locale, { numeric: "auto" });
+
+ if (years > 0) {
+ return rtf.format(0 - years, "year");
+ }
+ if (months > 0) {
+ return rtf.format(0 - months, "month");
+ }
+ if (days > 0) {
+ return rtf.format(0 - days, "day");
+ }
+ if (hours > 0) {
+ return rtf.format(0 - hours, "hour");
+ }
+ if (minutes > 0) {
+ return rtf.format(0 - minutes, "minute");
+ }
+ return rtf.format(Math.floor(0 - diff), "second");
};
export const TimeAgo = ({ timestamp }: TimeAgoProps): JSX.Element => {
const [timeAgo, setTimeAgo] = useState(getTimeAgo(timestamp));
- useEffect(() => {
- const interval = setInterval(() => {
- setTimeAgo(getTimeAgo(timestamp));
- }, 10000);
-
- return () => clearInterval(interval);
- }, [timestamp]);
-
useEffect(() => {
setTimeAgo(getTimeAgo(timestamp));
}, [timestamp]);
From 9d9c46f732251f8ca682826ebdc76ada27a2115b Mon Sep 17 00:00:00 2001
From: Tilen Komel
Date: Sun, 2 Feb 2025 14:10:48 +0100
Subject: [PATCH 21/31] Remove unused ariables from Nodes.tsx
---
src/pages/Nodes.tsx | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)
diff --git a/src/pages/Nodes.tsx b/src/pages/Nodes.tsx
index 6da0b246..ff178b4a 100644
--- a/src/pages/Nodes.tsx
+++ b/src/pages/Nodes.tsx
@@ -1,16 +1,14 @@
import { NodeOptionsDialog } from "@app/components/Dialog/NodeOptionsDialog";
import { TracerouteResponseDialog } from "@app/components/Dialog/TracerouteResponseDialog";
import Footer from "@app/components/UI/Footer";
-import { useAppStore } from "@app/core/stores/appStore";
import { Sidebar } from "@components/Sidebar.tsx";
-import { Button } from "@components/UI/Button.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 { Protobuf, type Types } from "@meshtastic/js";
import { numberToHexUnpadded } from "@noble/curves/abstract/utils";
-import { LockIcon, LockOpenIcon, TrashIcon } from "lucide-react";
+import { LockIcon, LockOpenIcon } from "lucide-react";
import { Fragment, type JSX, useCallback, useEffect, useState } from "react";
import { base16 } from "rfc4648";
@@ -20,8 +18,7 @@ export interface DeleteNoteDialogProps {
}
const NodesPage = (): JSX.Element => {
- const { nodes, hardware, setDialogOpen, connection } = useDevice();
- const { setNodeNumToBeRemoved } = useAppStore();
+ const { nodes, hardware, connection } = useDevice();
const [selectedNode, setSelectedNode] = useState<
Protobuf.Mesh.NodeInfo | undefined
>(undefined);
From 28c5fd64fe240847ddc857ab043dba5a49fc741f Mon Sep 17 00:00:00 2001
From: Tilen Komel
Date: Sun, 2 Feb 2025 16:05:35 +0100
Subject: [PATCH 22/31] Add overflow-x-scroll to Node details dialog raw
metrics
---
src/components/Dialog/NodeDetailsDialog.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/Dialog/NodeDetailsDialog.tsx b/src/components/Dialog/NodeDetailsDialog.tsx
index 8c30d0df..09f96e12 100644
--- a/src/components/Dialog/NodeDetailsDialog.tsx
+++ b/src/components/Dialog/NodeDetailsDialog.tsx
@@ -152,7 +152,7 @@ export const NodeDetailsDialog = ({
All Raw Metrics:
-
+
{JSON.stringify(device, null, 2)}
From 76de3e0e02f5d9bc90cf473b83506bd0df8a4616 Mon Sep 17 00:00:00 2001
From: Tilen Komel
Date: Mon, 3 Feb 2025 17:19:27 +0100
Subject: [PATCH 23/31] Biome
---
src/pages/Nodes.tsx | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/src/pages/Nodes.tsx b/src/pages/Nodes.tsx
index 18ed858c..135f0cbb 100644
--- a/src/pages/Nodes.tsx
+++ b/src/pages/Nodes.tsx
@@ -8,7 +8,7 @@ import { TimeAgo } from "@components/generic/TimeAgo.tsx";
import { useDevice } from "@core/stores/deviceStore.ts";
import { Protobuf, type Types } from "@meshtastic/js";
import { numberToHexUnpadded } from "@noble/curves/abstract/utils";
-import { LockIcon, LockOpenIcon, TrashIcon } from "lucide-react";
+import { LockIcon, LockOpenIcon } from "lucide-react";
import { Fragment, type JSX, useCallback, useEffect, useState } from "react";
import { base16 } from "rfc4648";
@@ -18,14 +18,13 @@ export interface DeleteNoteDialogProps {
}
const NodesPage = (): JSX.Element => {
- const { nodes, hardware, setDialogOpen } = useDevice();
+ const { nodes, hardware, connection } = useDevice();
const [selectedNode, setSelectedNode] = useState<
Protobuf.Mesh.NodeInfo | undefined
>(undefined);
const [selectedTraceroute, setSelectedTraceroute] = useState<
Types.PacketMetadata | undefined
>();
- const { setNodeNumToBeRemoved } = useAppStore();
const [searchTerm, setSearchTerm] = useState("");
const filteredNodes = Array.from(nodes.values()).filter((node) => {
From fb1b4c6cc55de6f340b65c9b10ec7699963397c9 Mon Sep 17 00:00:00 2001
From: Tilen Komel
Date: Mon, 3 Feb 2025 18:27:22 +0100
Subject: [PATCH 24/31] Update according to change request
---
src/components/PageComponents/Messages/TraceRoute.tsx | 6 ++----
src/components/generic/TimeAgo.tsx | 10 ++--------
src/components/generic/Uptime.tsx | 10 ++--------
3 files changed, 6 insertions(+), 20 deletions(-)
diff --git a/src/components/PageComponents/Messages/TraceRoute.tsx b/src/components/PageComponents/Messages/TraceRoute.tsx
index 15e0dd00..11c77332 100644
--- a/src/components/PageComponents/Messages/TraceRoute.tsx
+++ b/src/components/PageComponents/Messages/TraceRoute.tsx
@@ -29,8 +29,7 @@ export const TraceRoute = ({
{to?.user?.longName}
↓ {snrTowards?.[0] ? snrTowards[0] : "??"}dB
{route.map((hop, i) => (
- // biome-ignore lint/suspicious/noArrayIndexKey:
-
+
{nodes.get(hop)?.user?.longName ?? `!${numberToHexUnpadded(hop)}`}
@@ -45,8 +44,7 @@ export const TraceRoute = ({
{from?.user?.longName}
↓ {snrBack?.[0] ? snrBack[0] : "??"}dB
{routeBack.map((hop, i) => (
- // biome-ignore lint/suspicious/noArrayIndexKey:
-
+
{nodes.get(hop)?.user?.longName ??
`!${numberToHexUnpadded(hop)}`}
diff --git a/src/components/generic/TimeAgo.tsx b/src/components/generic/TimeAgo.tsx
index e8d5973c..cd865dad 100755
--- a/src/components/generic/TimeAgo.tsx
+++ b/src/components/generic/TimeAgo.tsx
@@ -1,4 +1,4 @@
-import { type JSX, useEffect, useState } from "react";
+import type { JSX } from "react";
export interface TimeAgoProps {
timestamp: number;
@@ -37,11 +37,5 @@ const getTimeAgo = (
};
export const TimeAgo = ({ timestamp }: TimeAgoProps): JSX.Element => {
- const [timeAgo, setTimeAgo] = useState(getTimeAgo(timestamp));
-
- useEffect(() => {
- setTimeAgo(getTimeAgo(timestamp));
- }, [timestamp]);
-
- return {timeAgo};
+ return {getTimeAgo(timestamp)};
};
diff --git a/src/components/generic/Uptime.tsx b/src/components/generic/Uptime.tsx
index 59cceb4b..fdcf897a 100644
--- a/src/components/generic/Uptime.tsx
+++ b/src/components/generic/Uptime.tsx
@@ -1,4 +1,4 @@
-import { type JSX, useEffect, useState } from "react";
+import type { JSX } from "react";
export interface UptimeProps {
seconds: number;
@@ -13,11 +13,5 @@ const getUptime = (seconds: number): string => {
};
export const Uptime = ({ seconds }: UptimeProps): JSX.Element => {
- const [uptime, setUptime] = useState(getUptime(seconds));
-
- useEffect(() => {
- setUptime(getUptime(seconds));
- }, [seconds]);
-
- return {uptime};
+ return {getUptime(seconds)};
};
From 48eb931c37ac9aa5c35e029c5d66e8d390012e14 Mon Sep 17 00:00:00 2001
From: Tilen Komel
Date: Mon, 3 Feb 2025 19:07:27 +0100
Subject: [PATCH 25/31] Add time toltip to timeAgo
---
src/components/generic/TimeAgo.tsx | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/components/generic/TimeAgo.tsx b/src/components/generic/TimeAgo.tsx
index cd865dad..6148b3be 100755
--- a/src/components/generic/TimeAgo.tsx
+++ b/src/components/generic/TimeAgo.tsx
@@ -37,5 +37,9 @@ const getTimeAgo = (
};
export const TimeAgo = ({ timestamp }: TimeAgoProps): JSX.Element => {
- return {getTimeAgo(timestamp)};
+ return (
+
+ {getTimeAgo(timestamp)}
+
+ );
};
From 44b8dd308a27ce03160ada360fcd620162bfa4e2 Mon Sep 17 00:00:00 2001
From: Tilen Komel
Date: Mon, 3 Feb 2025 21:23:08 +0100
Subject: [PATCH 26/31] Update tooltip
---
src/components/generic/TimeAgo.tsx | 27 ++++++++++++++++++++++++---
1 file changed, 24 insertions(+), 3 deletions(-)
diff --git a/src/components/generic/TimeAgo.tsx b/src/components/generic/TimeAgo.tsx
index 6148b3be..85043c58 100755
--- a/src/components/generic/TimeAgo.tsx
+++ b/src/components/generic/TimeAgo.tsx
@@ -1,3 +1,10 @@
+import {
+ Tooltip,
+ TooltipContent,
+ TooltipPortal,
+ TooltipProvider,
+ TooltipTrigger,
+} from "@radix-ui/react-tooltip";
import type { JSX } from "react";
export interface TimeAgoProps {
@@ -38,8 +45,22 @@ const getTimeAgo = (
export const TimeAgo = ({ timestamp }: TimeAgoProps): JSX.Element => {
return (
-
- {getTimeAgo(timestamp)}
-
+
+
+
+ {getTimeAgo(timestamp)}
+
+
+
+ {new Date(timestamp).toLocaleString()}
+
+
+
+
);
};
From 5a1c207ffc45aa68bee91487499f345b820aaa34 Mon Sep 17 00:00:00 2001
From: Tilen Komel
Date: Tue, 4 Feb 2025 21:50:11 +0100
Subject: [PATCH 27/31] Remove useEffect
---
src/components/Dialog/NodeDetailsDialog.tsx | 8 +-------
1 file changed, 1 insertion(+), 7 deletions(-)
diff --git a/src/components/Dialog/NodeDetailsDialog.tsx b/src/components/Dialog/NodeDetailsDialog.tsx
index 09f96e12..80c032bc 100644
--- a/src/components/Dialog/NodeDetailsDialog.tsx
+++ b/src/components/Dialog/NodeDetailsDialog.tsx
@@ -15,7 +15,6 @@ import {
} from "@components/UI/Dialog";
import { Protobuf } from "@meshtastic/js";
import { numberToHexUnpadded } from "@noble/curves/abstract/utils";
-import { useEffect, useState } from "react";
import { DeviceImage } from "../generic/DeviceImage";
import { TimeAgo } from "../generic/TimeAgo";
import { Uptime } from "../generic/Uptime";
@@ -31,12 +30,7 @@ export const NodeDetailsDialog = ({
}: NodeDetailsDialogProps) => {
const { nodes } = useDevice();
const { nodeNumDetails } = useAppStore();
- const [device, setDevice] = useState(null);
-
- useEffect(() => {
- if (!nodeNumDetails) return;
- setDevice(nodes.get(nodeNumDetails));
- }, [nodeNumDetails, nodes]);
+ const device: Protobuf.Mesh.NodeInfo = nodes.get(nodeNumDetails);
return device ? (
From c2a2e0ac19926ca011e696428e8b274c877ab127 Mon Sep 17 00:00:00 2001
From: Tilen Komel
Date: Tue, 4 Feb 2025 22:52:14 +0100
Subject: [PATCH 28/31] Add direct message button
---
src/components/Dialog/NodeOptionsDialog.tsx | 19 +++++++++++++++++--
src/core/stores/appStore.ts | 15 +++++++++++++++
src/pages/Messages.tsx | 11 ++++-------
3 files changed, 36 insertions(+), 9 deletions(-)
diff --git a/src/components/Dialog/NodeOptionsDialog.tsx b/src/components/Dialog/NodeOptionsDialog.tsx
index 83a01426..d62e4065 100644
--- a/src/components/Dialog/NodeOptionsDialog.tsx
+++ b/src/components/Dialog/NodeOptionsDialog.tsx
@@ -24,8 +24,13 @@ export const NodeOptionsDialog = ({
open,
onOpenChange,
}: NodeOptionsDialogProps): JSX.Element => {
- const { setDialogOpen, connection } = useDevice();
- const { setNodeNumToBeRemoved, setNodeNumDetails } = useAppStore();
+ const { setDialogOpen, connection, setActivePage } = useDevice();
+ const {
+ setNodeNumToBeRemoved,
+ setNodeNumDetails,
+ setChatType,
+ setActiveChat,
+ } = useAppStore();
const longName =
node?.user?.longName ??
(node ? `!${numberToHexUnpadded(node?.num)}` : "Unknown");
@@ -33,6 +38,13 @@ export const NodeOptionsDialog = ({
node?.user?.shortName ??
(node ? `${numberToHexUnpadded(node?.num).substring(0, 4)}` : "UNK");
+ function handleDirectMessage() {
+ if (!node) return;
+ setChatType("direct");
+ setActiveChat(node.num);
+ setActivePage("messages");
+ }
+
function handleRequestPosition() {
if (!node) return;
toast({
@@ -66,6 +78,9 @@ export const NodeOptionsDialog = ({
{`${longName} (${shortName})`}
+
+
+
diff --git a/src/core/stores/appStore.ts b/src/core/stores/appStore.ts
index 2e311e6e..d9e213c8 100644
--- a/src/core/stores/appStore.ts
+++ b/src/core/stores/appStore.ts
@@ -1,3 +1,4 @@
+import { Types } from "@meshtastic/js";
import { produce } from "immer";
import { create } from "zustand";
@@ -30,6 +31,8 @@ interface AppState {
accent: AccentColor;
connectDialogOpen: boolean;
nodeNumDetails: number;
+ activeChat: number;
+ chatType: "broadcast" | "direct";
setRasterSources: (sources: RasterSource[]) => void;
addRasterSource: (source: RasterSource) => void;
@@ -44,6 +47,8 @@ interface AppState {
setAccent: (color: AccentColor) => void;
setConnectDialogOpen: (open: boolean) => void;
setNodeNumDetails: (nodeNum: number) => void;
+ setActiveChat: (chat: number) => void;
+ setChatType: (type: "broadcast" | "direct") => void;
}
export const useAppStore = create
()((set) => ({
@@ -60,6 +65,8 @@ export const useAppStore = create()((set) => ({
connectDialogOpen: false,
nodeNumToBeRemoved: 0,
nodeNumDetails: 0,
+ activeChat: Types.ChannelNumber.Primary,
+ chatType: "broadcast",
setRasterSources: (sources: RasterSource[]) => {
set(
@@ -131,4 +138,12 @@ export const useAppStore = create()((set) => ({
set((state) => ({
nodeNumDetails: nodeNum,
})),
+ setActiveChat: (chat) =>
+ set(() => ({
+ activeChat: chat,
+ })),
+ setChatType: (type) =>
+ set(() => ({
+ chatType: type,
+ })),
}));
diff --git a/src/pages/Messages.tsx b/src/pages/Messages.tsx
index c06ce08f..7fe99f69 100644
--- a/src/pages/Messages.tsx
+++ b/src/pages/Messages.tsx
@@ -1,3 +1,4 @@
+import { useAppStore } from "@app/core/stores/appStore";
import { ChannelChat } from "@components/PageComponents/Messages/ChannelChat.tsx";
import { PageLayout } from "@components/PageLayout.tsx";
import { Sidebar } from "@components/Sidebar.tsx";
@@ -5,21 +6,17 @@ import { Avatar } from "@components/UI/Avatar.tsx";
import { SidebarSection } from "@components/UI/Sidebar/SidebarSection.tsx";
import { SidebarButton } from "@components/UI/Sidebar/sidebarButton.tsx";
import { useToast } from "@core/hooks/useToast.ts";
-import { Device, useDevice, useDeviceStore } from "@core/stores/deviceStore.ts";
+import { useDevice } from "@core/stores/deviceStore.ts";
import { Protobuf, Types } from "@meshtastic/js";
import { numberToHexUnpadded } from "@noble/curves/abstract/utils";
import { getChannelName } from "@pages/Channels.tsx";
import { HashIcon, LockIcon, LockOpenIcon, WaypointsIcon } from "lucide-react";
import { useState } from "react";
-const MessagesPage = () => {
+export const MessagesPage = () => {
const { channels, nodes, hardware, messages, traceroutes, connection } =
useDevice();
- const [chatType, setChatType] =
- useState("broadcast");
- const [activeChat, setActiveChat] = useState(
- Types.ChannelNumber.Primary,
- );
+ const { activeChat, chatType, setActiveChat, setChatType } = useAppStore();
const [searchTerm, setSearchTerm] = useState("");
const filteredNodes = Array.from(nodes.values()).filter((node) => {
if (node.num === hardware.myNodeNum) return false;
From b8c1096568cda1f20186313c1b8fda8dbce9a0a3 Mon Sep 17 00:00:00 2001
From: Tilen Komel
Date: Wed, 5 Feb 2025 21:06:55 +0100
Subject: [PATCH 29/31] Add short name row to nodes page
---
src/pages/Nodes.tsx | 18 ++++++++++++++++--
1 file changed, 16 insertions(+), 2 deletions(-)
diff --git a/src/pages/Nodes.tsx b/src/pages/Nodes.tsx
index 135f0cbb..a47877ab 100644
--- a/src/pages/Nodes.tsx
+++ b/src/pages/Nodes.tsx
@@ -64,7 +64,8 @@ const NodesPage = (): JSX.Element => {
{
/>,
setSelectedNode(node)}
+ className="cursor-pointer"
+ >
+ {node.user?.shortName ??
+ (node.user?.macaddr
+ ? `${base16
+ .stringify(node.user?.macaddr.subarray(4, 6) ?? [])
+ .toLowerCase()}`
+ : `${numberToHexUnpadded(node.num).slice(-4)}`)}
+
,
+
+ setSelectedNode(node)}
className="cursor-pointer"
>
From 175e98a08081c78007f0ee6c17f97b2d4418f33f Mon Sep 17 00:00:00 2001
From: Tilen Komel
Date: Wed, 5 Feb 2025 21:45:31 +0100
Subject: [PATCH 30/31] Add location respone dialog
---
.../Dialog/LocationResponseDialog.tsx | 62 +++++++++++++++++++
src/pages/Nodes.tsx | 29 ++++++++-
2 files changed, 89 insertions(+), 2 deletions(-)
create mode 100644 src/components/Dialog/LocationResponseDialog.tsx
diff --git a/src/components/Dialog/LocationResponseDialog.tsx b/src/components/Dialog/LocationResponseDialog.tsx
new file mode 100644
index 00000000..825046e6
--- /dev/null
+++ b/src/components/Dialog/LocationResponseDialog.tsx
@@ -0,0 +1,62 @@
+import { useDevice } from "@app/core/stores/deviceStore";
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogHeader,
+ DialogTitle,
+} from "@components/UI/Dialog";
+import type { Protobuf, Types } from "@meshtastic/js";
+import { numberToHexUnpadded } from "@noble/curves/abstract/utils";
+import type { JSX } from "react";
+
+export interface LocationResponseDialogProps {
+ location: Types.PacketMetadata | undefined;
+ open: boolean;
+ onOpenChange: () => void;
+}
+
+export const LocationResponseDialog = ({
+ location,
+ open,
+ onOpenChange,
+}: LocationResponseDialogProps): JSX.Element => {
+ const { nodes } = useDevice();
+
+ const from = nodes.get(location?.from ?? 0);
+ const longName =
+ from?.user?.longName ??
+ (from ? `!${numberToHexUnpadded(from?.num)}` : "Unknown");
+ const shortName =
+ from?.user?.shortName ??
+ (from ? `${numberToHexUnpadded(from?.num).substring(0, 4)}` : "UNK");
+
+ return (
+
+
+
+ {`Location: ${longName} (${shortName})`}
+
+
+
+
+
+
+ );
+};
diff --git a/src/pages/Nodes.tsx b/src/pages/Nodes.tsx
index a47877ab..fa1c5788 100644
--- a/src/pages/Nodes.tsx
+++ b/src/pages/Nodes.tsx
@@ -1,3 +1,4 @@
+import { LocationResponseDialog } from "@app/components/Dialog/LocationResponseDialog";
import { NodeOptionsDialog } from "@app/components/Dialog/NodeOptionsDialog";
import { TracerouteResponseDialog } from "@app/components/Dialog/TracerouteResponseDialog";
import Footer from "@app/components/UI/Footer";
@@ -25,6 +26,9 @@ const NodesPage = (): JSX.Element => {
const [selectedTraceroute, setSelectedTraceroute] = useState<
Types.PacketMetadata | undefined
>();
+ const [selectedLocation, setSelectedLocation] = useState<
+ Types.PacketMetadata | undefined
+ >();
const [searchTerm, setSearchTerm] = useState("");
const filteredNodes = Array.from(nodes.values()).filter((node) => {
@@ -34,9 +38,10 @@ const NodesPage = (): JSX.Element => {
});
useEffect(() => {
- connection?.events.onTraceRoutePacket.subscribe(handleTraceroute);
+ if (!connection) return;
+ connection.events.onTraceRoutePacket.subscribe(handleTraceroute);
return () => {
- connection?.events.onTraceRoutePacket.unsubscribe(handleTraceroute);
+ connection.events.onTraceRoutePacket.unsubscribe(handleTraceroute);
};
}, [connection]);
@@ -47,6 +52,21 @@ const NodesPage = (): JSX.Element => {
[],
);
+ useEffect(() => {
+ if (!connection) return;
+ connection.events.onPositionPacket.subscribe(handleLocation);
+ return () => {
+ connection.events.onPositionPacket.subscribe(handleLocation);
+ };
+ }, [connection]);
+
+ const handleLocation = useCallback(
+ (location: Types.PacketMetadata) => {
+ setSelectedLocation(location);
+ },
+ [],
+ );
+
return (
<>
@@ -155,6 +175,11 @@ const NodesPage = (): JSX.Element => {
open={!!selectedTraceroute}
onOpenChange={() => setSelectedTraceroute(undefined)}
/>
+ setSelectedLocation(undefined)}
+ />
From 30d36789c18adbd1dd2f02767567ed9fb7a307ea Mon Sep 17 00:00:00 2001
From: Tilen Komel
Date: Tue, 18 Feb 2025 16:51:29 +0100
Subject: [PATCH 31/31] Biome
---
src/pages/Nodes.tsx | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/src/pages/Nodes.tsx b/src/pages/Nodes.tsx
index 7c002136..6ad69f65 100644
--- a/src/pages/Nodes.tsx
+++ b/src/pages/Nodes.tsx
@@ -108,8 +108,8 @@ const NodesPage = (): JSX.Element => {
{node.user?.shortName ??
(node.user?.macaddr
? `${base16
- .stringify(node.user?.macaddr.subarray(4, 6) ?? [])
- .toLowerCase()}`
+ .stringify(node.user?.macaddr.subarray(4, 6) ?? [])
+ .toLowerCase()}`
: `${numberToHexUnpadded(node.num).slice(-4)}`)}
,
@@ -121,8 +121,8 @@ const NodesPage = (): JSX.Element => {
{node.user?.longName ??
(node.user?.macaddr
? `Meshtastic ${base16
- .stringify(node.user?.macaddr.subarray(4, 6) ?? [])
- .toLowerCase()}`
+ .stringify(node.user?.macaddr.subarray(4, 6) ?? [])
+ .toLowerCase()}`
: `!${numberToHexUnpadded(node.num)}`)}
,
@@ -158,8 +158,9 @@ const NodesPage = (): JSX.Element => {
{node.lastHeard !== 0
? node.viaMqtt === false && node.hopsAway === 0
? "Direct"
- : `${node.hopsAway.toString()} ${node.hopsAway > 1 ? "hops" : "hop"
- } away`
+ : `${node.hopsAway.toString()} ${
+ node.hopsAway > 1 ? "hops" : "hop"
+ } away`
: "-"}
{node.viaMqtt === true ? ", via MQTT" : ""}
,