From c4565d97b08b4e20e8055adad62ea30ce4132fec Mon Sep 17 00:00:00 2001
From: Hunter Thornsberry
Date: Fri, 21 Jun 2024 15:06:19 -0400
Subject: [PATCH 01/51] add ability to enable/disable HTTPS connections
---
.../PageComponents/Connect/HTTP.tsx | 20 ++++++-------------
1 file changed, 6 insertions(+), 14 deletions(-)
diff --git a/src/components/PageComponents/Connect/HTTP.tsx b/src/components/PageComponents/Connect/HTTP.tsx
index 30e70169..e5179bc1 100644
--- a/src/components/PageComponents/Connect/HTTP.tsx
+++ b/src/components/PageComponents/Connect/HTTP.tsx
@@ -24,17 +24,13 @@ export const HTTP = ({ closeDialog }: TabElementProps): JSX.Element => {
)
? "meshtastic.local"
: window.location.hostname,
- tls: location.protocol === "https:",
+ tls: false,
},
});
- const tlsEnabled = useWatch({
- control,
- name: "tls",
- defaultValue: location.protocol === "https:",
- });
const [connectionInProgress, setConnectionInProgress] = useState(false);
+ const [https, setHTTPS] = useState(false);
const onSubmit = handleSubmit(async (data) => {
setConnectionInProgress(true);
@@ -46,7 +42,7 @@ export const HTTP = ({ closeDialog }: TabElementProps): JSX.Element => {
await connection.connect({
address: data.ip,
fetchInterval: 2000,
- tls: data.tls,
+ tls: https,
});
setSelectedDevice(id);
@@ -60,8 +56,7 @@ export const HTTP = ({ closeDialog }: TabElementProps): JSX.Element => {
{
control={control}
render={({ field: { value, ...rest } }) => (
<>
-
+
{checked ? setHTTPS(true) : setHTTPS(false) }}
{...rest}
/>
>
From d8261a649bbdd10d7c676353a8ddbbec78c9256d Mon Sep 17 00:00:00 2001
From: Hunter Thornsberry
Date: Fri, 21 Jun 2024 15:09:29 -0400
Subject: [PATCH 02/51] add disabled
---
src/components/PageComponents/Connect/HTTP.tsx | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/components/PageComponents/Connect/HTTP.tsx b/src/components/PageComponents/Connect/HTTP.tsx
index e5179bc1..50bdfdcc 100644
--- a/src/components/PageComponents/Connect/HTTP.tsx
+++ b/src/components/PageComponents/Connect/HTTP.tsx
@@ -69,6 +69,7 @@ export const HTTP = ({ closeDialog }: TabElementProps): JSX.Element => {
{checked ? setHTTPS(true) : setHTTPS(false) }}
+ disabled={connectionInProgress}
{...rest}
/>
>
From fd9e327c85544fc8c30fe77fb99746591d494f5a Mon Sep 17 00:00:00 2001
From: sgtwilko
Date: Fri, 30 Aug 2024 01:01:44 +0100
Subject: [PATCH 03/51] Use window.location.host so that websites run on
non-standard ports include the port
---
src/components/PageComponents/Connect/HTTP.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/PageComponents/Connect/HTTP.tsx b/src/components/PageComponents/Connect/HTTP.tsx
index 1eb7ca5c..ae049be2 100644
--- a/src/components/PageComponents/Connect/HTTP.tsx
+++ b/src/components/PageComponents/Connect/HTTP.tsx
@@ -23,7 +23,7 @@ export const HTTP = ({ closeDialog }: TabElementProps): JSX.Element => {
window.location.hostname,
)
? "meshtastic.local"
- : window.location.hostname,
+ : window.location.host,
tls: location.protocol === "https:",
},
});
From bd48b02ef30078ea591d092c226784d581e96be8 Mon Sep 17 00:00:00 2001
From: Tilen Komel
Date: Wed, 18 Sep 2024 11:17:18 +0200
Subject: [PATCH 04/51] 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 05/51] 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 06/51] 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 07/51] 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 08/51] 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 09/51] 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 10/51] 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 11/51] 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 12/51] 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 13/51] 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 14/51] 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 15/51] Move remove button to quick options
---
src/components/Dialog/NodeOptionsDialog.tsx | 36 +++++++++++++++------
src/pages/Nodes.tsx | 18 +++--------
2 files changed, 31 insertions(+), 23 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,27 +33,27 @@ export const NodeOptionsDialog = ({
node?.user?.shortName ??
(node ? `${numberToHexUnpadded(node?.num).substring(0, 4)}` : "UNK");
- function handleTraceroute() {
+ function handleRequestPosition() {
if (!node) return;
toast({
- title: "Sending Traceroute, please wait...",
+ title: "Requesting position, please wait...",
});
- connection?.traceRoute(node.num).then(() =>
+ connection?.requestPosition(node.num).then(() =>
toast({
- title: "Traceroute sent.",
+ title: "Position request sent.",
}),
);
onOpenChange();
}
- function handleRequestPosition() {
+ function handleTraceroute() {
if (!node) return;
toast({
- title: "Requesting position, please wait...",
+ title: "Sending Traceroute, please wait...",
});
- connection?.requestPosition(node.num).then(() =>
+ connection?.traceRoute(node.num).then(() =>
toast({
- title: "Position request sent.",
+ title: "Traceroute sent.",
}),
);
onOpenChange();
@@ -63,11 +66,24 @@ export const NodeOptionsDialog = ({
{`${longName} (${shortName})`}
+
+
+
-
+
diff --git a/src/pages/Nodes.tsx b/src/pages/Nodes.tsx
index d9526481..5e3b48ac 100644
--- a/src/pages/Nodes.tsx
+++ b/src/pages/Nodes.tsx
@@ -62,7 +62,6 @@ const NodesPage = (): JSX.Element => {
{ title: "SNR", type: "normal", sortable: true },
{ title: "Encryption", type: "normal", sortable: false },
{ title: "Connection", type: "normal", sortable: true },
- { title: "Remove", type: "normal", sortable: false },
]}
rows={filteredNodes.map((node) => [
{
className="h-3 w-3 rounded-full bg-accent"
/>,
- setSelectedNode(node)}>
+ setSelectedNode(node)}
+ className="cursor-pointer"
+ >
{node.user?.longName ??
(node.user?.macaddr
? `Meshtastic ${base16
@@ -117,17 +120,6 @@ const NodesPage = (): JSX.Element => {
: "-"}
{node.viaMqtt === true ? ", via MQTT" : ""}
,
- ,
])}
/>
Date: Thu, 30 Jan 2025 15:04:07 +0100
Subject: [PATCH 16/51] Remove timeago-react
---
package.json | 1 -
pnpm-lock.yaml | 18 ----------
.../PageComponents/Map/NodeDetail.tsx | 2 +-
src/components/generic/Table/tmp/TimeAgo.tsx | 9 -----
src/components/generic/TimeAgo.tsx | 34 +++++++++++++++++++
src/pages/Nodes.tsx | 11 +++---
6 files changed, 40 insertions(+), 35 deletions(-)
delete mode 100755 src/components/generic/Table/tmp/TimeAgo.tsx
create mode 100755 src/components/generic/TimeAgo.tsx
diff --git a/package.json b/package.json
index 1c055073..ec62a1b4 100644
--- a/package.json
+++ b/package.json
@@ -61,7 +61,6 @@
"react-map-gl": "7.1.9",
"react-qrcode-logo": "^3.0.0",
"rfc4648": "^1.5.4",
- "timeago-react": "^3.0.6",
"vite-plugin-node-polyfills": "^0.23.0",
"zustand": "5.0.3"
},
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index f0d0f030..7a2029e9 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -113,9 +113,6 @@ importers:
rfc4648:
specifier: ^1.5.4
version: 1.5.4
- timeago-react:
- specifier: ^3.0.6
- version: 3.0.6(react@19.0.0)
vite-plugin-node-polyfills:
specifier: ^0.23.0
version: 0.23.0(rollup@4.29.1)(vite@5.3.6(@types/node@22.12.0))
@@ -2817,14 +2814,6 @@ packages:
through2@4.0.2:
resolution: {integrity: sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==}
- timeago-react@3.0.6:
- resolution: {integrity: sha512-4ywnCX3iFjdp84WPK7gt8s4n0FxXbYM+xv8hYL73p83dpcMxzmO+0W4xJuxflnkWNvum5aEaqTe6LZ3lUIudjQ==}
- peerDependencies:
- react: ^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0
-
- timeago.js@4.0.2:
- resolution: {integrity: sha512-a7wPxPdVlQL7lqvitHGGRsofhdwtkoSXPGATFuSOA2i1ZNQEPLrGnj68vOp2sOJTCFAQVXPeNMX/GctBaO9L2w==}
-
timers-browserify@2.0.12:
resolution: {integrity: sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==}
engines: {node: '>=0.6.0'}
@@ -6498,13 +6487,6 @@ snapshots:
dependencies:
readable-stream: 3.6.2
- timeago-react@3.0.6(react@19.0.0):
- dependencies:
- react: 19.0.0
- timeago.js: 4.0.2
-
- timeago.js@4.0.2: {}
-
timers-browserify@2.0.12:
dependencies:
setimmediate: 1.0.5
diff --git a/src/components/PageComponents/Map/NodeDetail.tsx b/src/components/PageComponents/Map/NodeDetail.tsx
index 5203d077..0aba4d4b 100644
--- a/src/components/PageComponents/Map/NodeDetail.tsx
+++ b/src/components/PageComponents/Map/NodeDetail.tsx
@@ -3,7 +3,7 @@ import { H5 } from "@app/components/UI/Typography/H5.tsx";
import { Subtle } from "@app/components/UI/Typography/Subtle.tsx";
import { Avatar } from "@components/UI/Avatar";
import { Mono } from "@components/generic/Mono.tsx";
-import { TimeAgo } from "@components/generic/Table/tmp/TimeAgo.tsx";
+import { TimeAgo } from "@components/generic/TimeAgo.tsx";
import { Protobuf } from "@meshtastic/js";
import type { Protobuf as ProtobufType } from "@meshtastic/js";
import { numberToHexUnpadded } from "@noble/curves/abstract/utils";
diff --git a/src/components/generic/Table/tmp/TimeAgo.tsx b/src/components/generic/Table/tmp/TimeAgo.tsx
deleted file mode 100755
index 32a766ff..00000000
--- a/src/components/generic/Table/tmp/TimeAgo.tsx
+++ /dev/null
@@ -1,9 +0,0 @@
-import TimeAgoReact from "timeago-react";
-
-export interface TimeAgoProps {
- timestamp: number;
-}
-
-export const TimeAgo = ({ timestamp }: TimeAgoProps): JSX.Element => {
- return ;
-};
diff --git a/src/components/generic/TimeAgo.tsx b/src/components/generic/TimeAgo.tsx
new file mode 100755
index 00000000..723ebf19
--- /dev/null
+++ b/src/components/generic/TimeAgo.tsx
@@ -0,0 +1,34 @@
+import { type JSX, useEffect, useState } from "react";
+
+export interface TimeAgoProps {
+ timestamp: number;
+}
+
+const getTimeAgo = (timestamp: number): string => {
+ const now = Date.now();
+ const secondsPast = Math.floor((now - timestamp) / 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`;
+};
+
+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]);
+
+ return {timeAgo};
+};
diff --git a/src/pages/Nodes.tsx b/src/pages/Nodes.tsx
index 5e3b48ac..7b0973f5 100644
--- a/src/pages/Nodes.tsx
+++ b/src/pages/Nodes.tsx
@@ -6,7 +6,7 @@ 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/Table/tmp/TimeAgo.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";
@@ -77,8 +77,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)}`)}
,
@@ -114,9 +114,8 @@ 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" : ""}
,
From fad6f72dd1e9689ea768b9784dc6859bbcfbb100 Mon Sep 17 00:00:00 2001
From: Tilen Komel
Date: Thu, 30 Jan 2025 15:17:05 +0100
Subject: [PATCH 17/51] Add DeviceImage.tsx and hardware svgs
---
public/devices/README.md | 5 +
public/devices/diy.svg | 1 +
public/devices/heltec-ht62-esp32c3-sx1262.svg | 1 +
public/devices/heltec-mesh-node-t114-case.svg | 1 +
public/devices/heltec-mesh-node-t114.svg | 1 +
public/devices/heltec-v3-case.svg | 1 +
public/devices/heltec-v3.svg | 1 +
public/devices/heltec-vision-master-e213.svg | 1 +
public/devices/heltec-vision-master-e290.svg | 1 +
public/devices/heltec-vision-master-t190.svg | 1 +
public/devices/heltec-wireless-paper-V1_0.svg | 1 +
public/devices/heltec-wireless-paper.svg | 1 +
.../devices/heltec-wireless-tracker-V1-0.svg | 1 +
public/devices/heltec-wireless-tracker.svg | 1 +
public/devices/heltec-wsl-v3.svg | 1 +
public/devices/nano-g2-ultra.svg | 1 +
public/devices/pico.svg | 2956 ++++++++++++++
public/devices/promicro.svg | 1 +
public/devices/rak-wismeshtap.svg | 1 +
public/devices/rak11310.svg | 2339 +++++++++++
public/devices/rak2560.svg | 1 +
public/devices/rak4631.svg | 3514 +++++++++++++++++
public/devices/rak4631_case.svg | 1 +
public/devices/rpipicow.svg | 1 +
public/devices/seeed-sensecap-indicator.svg | 1 +
public/devices/seeed-xiao-s3.svg | 1 +
public/devices/station-g2.svg | 1 +
public/devices/t-deck.svg | 1 +
public/devices/t-echo.svg | 1 +
public/devices/t-watch-s3.svg | 1 +
public/devices/tbeam-s3-core.svg | 1 +
public/devices/tbeam.svg | 1 +
public/devices/tlora-c6.svg | 1 +
public/devices/tlora-t3s3-epaper.svg | 1 +
public/devices/tlora-t3s3-v1.svg | 1 +
public/devices/tlora-v2-1-1_6.svg | 1 +
public/devices/tlora-v2-1-1_8.svg | 1 +
public/devices/tracker-t1000-e.svg | 1 +
public/devices/unknown.svg | 160 +
public/devices/wio-tracker-wm1110.svg | 1 +
public/devices/wm1110_dev_kit.svg | 1 +
src/components/generic/DeviceImage.tsx | 53 +
src/pages/Nodes.tsx | 9 +-
43 files changed, 9068 insertions(+), 4 deletions(-)
create mode 100644 public/devices/README.md
create mode 100644 public/devices/diy.svg
create mode 100644 public/devices/heltec-ht62-esp32c3-sx1262.svg
create mode 100644 public/devices/heltec-mesh-node-t114-case.svg
create mode 100644 public/devices/heltec-mesh-node-t114.svg
create mode 100644 public/devices/heltec-v3-case.svg
create mode 100644 public/devices/heltec-v3.svg
create mode 100644 public/devices/heltec-vision-master-e213.svg
create mode 100644 public/devices/heltec-vision-master-e290.svg
create mode 100644 public/devices/heltec-vision-master-t190.svg
create mode 100644 public/devices/heltec-wireless-paper-V1_0.svg
create mode 100644 public/devices/heltec-wireless-paper.svg
create mode 100644 public/devices/heltec-wireless-tracker-V1-0.svg
create mode 100644 public/devices/heltec-wireless-tracker.svg
create mode 100644 public/devices/heltec-wsl-v3.svg
create mode 100644 public/devices/nano-g2-ultra.svg
create mode 100644 public/devices/pico.svg
create mode 100644 public/devices/promicro.svg
create mode 100644 public/devices/rak-wismeshtap.svg
create mode 100644 public/devices/rak11310.svg
create mode 100644 public/devices/rak2560.svg
create mode 100644 public/devices/rak4631.svg
create mode 100644 public/devices/rak4631_case.svg
create mode 100644 public/devices/rpipicow.svg
create mode 100644 public/devices/seeed-sensecap-indicator.svg
create mode 100644 public/devices/seeed-xiao-s3.svg
create mode 100644 public/devices/station-g2.svg
create mode 100644 public/devices/t-deck.svg
create mode 100644 public/devices/t-echo.svg
create mode 100644 public/devices/t-watch-s3.svg
create mode 100644 public/devices/tbeam-s3-core.svg
create mode 100644 public/devices/tbeam.svg
create mode 100644 public/devices/tlora-c6.svg
create mode 100644 public/devices/tlora-t3s3-epaper.svg
create mode 100644 public/devices/tlora-t3s3-v1.svg
create mode 100644 public/devices/tlora-v2-1-1_6.svg
create mode 100644 public/devices/tlora-v2-1-1_8.svg
create mode 100644 public/devices/tracker-t1000-e.svg
create mode 100644 public/devices/unknown.svg
create mode 100644 public/devices/wio-tracker-wm1110.svg
create mode 100644 public/devices/wm1110_dev_kit.svg
create mode 100644 src/components/generic/DeviceImage.tsx
diff --git a/public/devices/README.md b/public/devices/README.md
new file mode 100644
index 00000000..0e40ce8d
--- /dev/null
+++ b/public/devices/README.md
@@ -0,0 +1,5 @@
+# Copyright Notice
+Copyright © 2024 Meshtastic LLC. All Rights Reserved.
+
+## In reference to the GNU GPLv3 License terms defined in Section 7e
+Images (or assets) in this directory are protected under international copyright laws and treaties. Unauthorized reproduction, distribution, modification, or use of these images in any form, commercial or otherwise, outside of official Meshtastic creative works or its Backers and Partners is strictly prohibited without prior written consent from the copyright holder (Meshtastic LLC).
diff --git a/public/devices/diy.svg b/public/devices/diy.svg
new file mode 100644
index 00000000..823467ed
--- /dev/null
+++ b/public/devices/diy.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/devices/heltec-ht62-esp32c3-sx1262.svg b/public/devices/heltec-ht62-esp32c3-sx1262.svg
new file mode 100644
index 00000000..c52534ef
--- /dev/null
+++ b/public/devices/heltec-ht62-esp32c3-sx1262.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/devices/heltec-mesh-node-t114-case.svg b/public/devices/heltec-mesh-node-t114-case.svg
new file mode 100644
index 00000000..b2abe639
--- /dev/null
+++ b/public/devices/heltec-mesh-node-t114-case.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/devices/heltec-mesh-node-t114.svg b/public/devices/heltec-mesh-node-t114.svg
new file mode 100644
index 00000000..779a8f6a
--- /dev/null
+++ b/public/devices/heltec-mesh-node-t114.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/devices/heltec-v3-case.svg b/public/devices/heltec-v3-case.svg
new file mode 100644
index 00000000..1b1d3c55
--- /dev/null
+++ b/public/devices/heltec-v3-case.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/devices/heltec-v3.svg b/public/devices/heltec-v3.svg
new file mode 100644
index 00000000..13a5fa64
--- /dev/null
+++ b/public/devices/heltec-v3.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/devices/heltec-vision-master-e213.svg b/public/devices/heltec-vision-master-e213.svg
new file mode 100644
index 00000000..2c1cca09
--- /dev/null
+++ b/public/devices/heltec-vision-master-e213.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/devices/heltec-vision-master-e290.svg b/public/devices/heltec-vision-master-e290.svg
new file mode 100644
index 00000000..ca7d296a
--- /dev/null
+++ b/public/devices/heltec-vision-master-e290.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/devices/heltec-vision-master-t190.svg b/public/devices/heltec-vision-master-t190.svg
new file mode 100644
index 00000000..55db34f9
--- /dev/null
+++ b/public/devices/heltec-vision-master-t190.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/devices/heltec-wireless-paper-V1_0.svg b/public/devices/heltec-wireless-paper-V1_0.svg
new file mode 100644
index 00000000..cb3f188d
--- /dev/null
+++ b/public/devices/heltec-wireless-paper-V1_0.svg
@@ -0,0 +1 @@
+
diff --git a/public/devices/heltec-wireless-paper.svg b/public/devices/heltec-wireless-paper.svg
new file mode 100644
index 00000000..cb3f188d
--- /dev/null
+++ b/public/devices/heltec-wireless-paper.svg
@@ -0,0 +1 @@
+
diff --git a/public/devices/heltec-wireless-tracker-V1-0.svg b/public/devices/heltec-wireless-tracker-V1-0.svg
new file mode 100644
index 00000000..a5392595
--- /dev/null
+++ b/public/devices/heltec-wireless-tracker-V1-0.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/devices/heltec-wireless-tracker.svg b/public/devices/heltec-wireless-tracker.svg
new file mode 100644
index 00000000..a5392595
--- /dev/null
+++ b/public/devices/heltec-wireless-tracker.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/devices/heltec-wsl-v3.svg b/public/devices/heltec-wsl-v3.svg
new file mode 100644
index 00000000..1741223e
--- /dev/null
+++ b/public/devices/heltec-wsl-v3.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/devices/nano-g2-ultra.svg b/public/devices/nano-g2-ultra.svg
new file mode 100644
index 00000000..6dbe47af
--- /dev/null
+++ b/public/devices/nano-g2-ultra.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/devices/pico.svg b/public/devices/pico.svg
new file mode 100644
index 00000000..82ce6526
--- /dev/null
+++ b/public/devices/pico.svg
@@ -0,0 +1,2956 @@
+
+
diff --git a/public/devices/promicro.svg b/public/devices/promicro.svg
new file mode 100644
index 00000000..3dc26021
--- /dev/null
+++ b/public/devices/promicro.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/devices/rak-wismeshtap.svg b/public/devices/rak-wismeshtap.svg
new file mode 100644
index 00000000..34e77876
--- /dev/null
+++ b/public/devices/rak-wismeshtap.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/devices/rak11310.svg b/public/devices/rak11310.svg
new file mode 100644
index 00000000..8f526a47
--- /dev/null
+++ b/public/devices/rak11310.svg
@@ -0,0 +1,2339 @@
+
+
diff --git a/public/devices/rak2560.svg b/public/devices/rak2560.svg
new file mode 100644
index 00000000..b8514f01
--- /dev/null
+++ b/public/devices/rak2560.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/devices/rak4631.svg b/public/devices/rak4631.svg
new file mode 100644
index 00000000..6dc2957a
--- /dev/null
+++ b/public/devices/rak4631.svg
@@ -0,0 +1,3514 @@
+
+
diff --git a/public/devices/rak4631_case.svg b/public/devices/rak4631_case.svg
new file mode 100644
index 00000000..a0b2bbb8
--- /dev/null
+++ b/public/devices/rak4631_case.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/devices/rpipicow.svg b/public/devices/rpipicow.svg
new file mode 100644
index 00000000..cb4b1f68
--- /dev/null
+++ b/public/devices/rpipicow.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/devices/seeed-sensecap-indicator.svg b/public/devices/seeed-sensecap-indicator.svg
new file mode 100644
index 00000000..f7bf9db0
--- /dev/null
+++ b/public/devices/seeed-sensecap-indicator.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/devices/seeed-xiao-s3.svg b/public/devices/seeed-xiao-s3.svg
new file mode 100644
index 00000000..04e97fe0
--- /dev/null
+++ b/public/devices/seeed-xiao-s3.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/devices/station-g2.svg b/public/devices/station-g2.svg
new file mode 100644
index 00000000..8d2e0aed
--- /dev/null
+++ b/public/devices/station-g2.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/devices/t-deck.svg b/public/devices/t-deck.svg
new file mode 100644
index 00000000..cdc53c5d
--- /dev/null
+++ b/public/devices/t-deck.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/devices/t-echo.svg b/public/devices/t-echo.svg
new file mode 100644
index 00000000..e178a50f
--- /dev/null
+++ b/public/devices/t-echo.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/devices/t-watch-s3.svg b/public/devices/t-watch-s3.svg
new file mode 100644
index 00000000..19084c19
--- /dev/null
+++ b/public/devices/t-watch-s3.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/devices/tbeam-s3-core.svg b/public/devices/tbeam-s3-core.svg
new file mode 100644
index 00000000..f42e6d2c
--- /dev/null
+++ b/public/devices/tbeam-s3-core.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/devices/tbeam.svg b/public/devices/tbeam.svg
new file mode 100644
index 00000000..cd0475c6
--- /dev/null
+++ b/public/devices/tbeam.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/devices/tlora-c6.svg b/public/devices/tlora-c6.svg
new file mode 100644
index 00000000..8b626638
--- /dev/null
+++ b/public/devices/tlora-c6.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/devices/tlora-t3s3-epaper.svg b/public/devices/tlora-t3s3-epaper.svg
new file mode 100644
index 00000000..6f2e8452
--- /dev/null
+++ b/public/devices/tlora-t3s3-epaper.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/devices/tlora-t3s3-v1.svg b/public/devices/tlora-t3s3-v1.svg
new file mode 100644
index 00000000..1f8847d4
--- /dev/null
+++ b/public/devices/tlora-t3s3-v1.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/devices/tlora-v2-1-1_6.svg b/public/devices/tlora-v2-1-1_6.svg
new file mode 100644
index 00000000..dbe36ef5
--- /dev/null
+++ b/public/devices/tlora-v2-1-1_6.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/devices/tlora-v2-1-1_8.svg b/public/devices/tlora-v2-1-1_8.svg
new file mode 100644
index 00000000..dbe36ef5
--- /dev/null
+++ b/public/devices/tlora-v2-1-1_8.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/devices/tracker-t1000-e.svg b/public/devices/tracker-t1000-e.svg
new file mode 100644
index 00000000..6f7a06c9
--- /dev/null
+++ b/public/devices/tracker-t1000-e.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/devices/unknown.svg b/public/devices/unknown.svg
new file mode 100644
index 00000000..1d2cd87b
--- /dev/null
+++ b/public/devices/unknown.svg
@@ -0,0 +1,160 @@
+
+
diff --git a/public/devices/wio-tracker-wm1110.svg b/public/devices/wio-tracker-wm1110.svg
new file mode 100644
index 00000000..15ace5c5
--- /dev/null
+++ b/public/devices/wio-tracker-wm1110.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/devices/wm1110_dev_kit.svg b/public/devices/wm1110_dev_kit.svg
new file mode 100644
index 00000000..94aefe30
--- /dev/null
+++ b/public/devices/wm1110_dev_kit.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/components/generic/DeviceImage.tsx b/src/components/generic/DeviceImage.tsx
new file mode 100644
index 00000000..ef96a429
--- /dev/null
+++ b/src/components/generic/DeviceImage.tsx
@@ -0,0 +1,53 @@
+export interface DeviceImageProps {
+ deviceType: string;
+ className?: React.HTMLAttributes["className"];
+}
+
+const hardwareModelToFilename: { [key: string]: string } = {
+ DIY_V1: "diy.svg",
+ NANO_G2_ULTRA: "nano-g2-ultra.svg",
+ TBEAM: "tbeam.svg",
+ HELTEC_HT62: "heltec-ht62-esp32c3-sx1262.svg",
+ RPI_PICO: "pico.svg",
+ T_DECK: "t-deck.svg",
+ HELTEC_MESH_NODE_T114: "heltec-mesh-node-t114.svg",
+ HELTEC_MESH_NODE_T114_CASE: "heltec-mesh-node-t114-case.svg",
+ HELTEC_V3: "heltec-v3.svg",
+ HELTEC_V3_CASE: "heltec-v3-case.svg",
+ HELTEC_VISION_MASTER_E213: "heltec-vision-master-e213.svg",
+ HELTEC_VISION_MASTER_E290: "heltec-vision-master-e290.svg",
+ HELTEC_VISION_MASTER_T190: "heltec-vision-master-t190.svg",
+ HELTEC_WIRELESS_PAPER: "heltec-wireless-paper.svg",
+ HELTEC_WIRELESS_PAPER_V1_0: "heltec-wireless-paper-V1_0.svg",
+ HELTEC_WIRELESS_TRACKER: "heltec-wireless-tracker.svg",
+ HELTEC_WIRELESS_TRACKER_V1_0: "heltec-wireless-tracker-V1-0.svg",
+ HELTEC_WSL_V3: "heltec-wsl-v3.svg",
+ TLORA_C6: "tlora-c6.svg",
+ TLORA_T3_S3: "tlora-t3s3-v1.svg",
+ TLORA_T3_S3_EPAPER: "tlora-t3s3-epaper.svg",
+ TLORA_V2: "tlora-v2-1-1_6.svg",
+ TLORA_V2_1_1P6: "tlora-v2-1-1_6.svg",
+ TLORA_V2_1_1P8: "tlora-v2-1-1_8.svg",
+ RAK11310: "rak11310.svg",
+ RAK2560: "rak2560.svg",
+ RAK4631: "rak4631.svg",
+ RAK4631_CASE: "rak4631_case.svg",
+ WIO_WM1110: "wio-tracker-wm1110.svg",
+ WM1110_DEV_KIT: "wm1110_dev_kit.svg",
+ STATION_G2: "station-g2.svg",
+ TBEAM_V0P7: "tbeam-s3-core.svg",
+ T_ECHO: "t-echo.svg",
+ TRACKER_T1000_E: "tracker-t1000-e.svg",
+ T_WATCH_S3: "t-watch-s3.svg",
+ SEEED_XIAO_S3: "seeed-xiao-s3.svg",
+ SENSECAP_INDICATOR: "seeed-sensecap-indicator.svg",
+ PROMICRO: "promicro.svg",
+ RPIPICOW: "rpipicow.svg",
+ UNKNOWN: "unknown.svg",
+};
+
+export const DeviceImage = ({ deviceType, className }: DeviceImageProps) => {
+ const getPath = (device: string) => `/devices/${device}`;
+ const device = hardwareModelToFilename[deviceType] || "unknown.svg";
+ return
;
+};
diff --git a/src/pages/Nodes.tsx b/src/pages/Nodes.tsx
index 7b0973f5..6da0b246 100644
--- a/src/pages/Nodes.tsx
+++ b/src/pages/Nodes.tsx
@@ -77,8 +77,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)}`)}
,
@@ -114,8 +114,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" : ""}
,
From 97d206a9d4f84edded79198cf1ac4e9441fb5abe Mon Sep 17 00:00:00 2001
From: Tilen Komel
Date: Thu, 30 Jan 2025 15:25:54 +0100
Subject: [PATCH 18/51] Add Uptime.tsx
---
src/components/generic/Uptime.tsx | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
create mode 100644 src/components/generic/Uptime.tsx
diff --git a/src/components/generic/Uptime.tsx b/src/components/generic/Uptime.tsx
new file mode 100644
index 00000000..59cceb4b
--- /dev/null
+++ b/src/components/generic/Uptime.tsx
@@ -0,0 +1,23 @@
+import { type JSX, useEffect, useState } from "react";
+
+export interface UptimeProps {
+ seconds: number;
+}
+
+const getUptime = (seconds: number): string => {
+ const days = Math.floor(seconds / 86400);
+ const hours = Math.floor((seconds % 86400) / 3600);
+ const minutes = Math.floor(((seconds % 86400) % 3600) / 60);
+ const secondsLeft = Math.floor(((seconds % 86400) % 3600) % 60);
+ return `${days}d ${hours}h ${minutes}m ${secondsLeft}s`;
+};
+
+export const Uptime = ({ seconds }: UptimeProps): JSX.Element => {
+ const [uptime, setUptime] = useState(getUptime(seconds));
+
+ useEffect(() => {
+ setUptime(getUptime(seconds));
+ }, [seconds]);
+
+ return {uptime};
+};
From 8ac714a5b5cd9eb43fce6444f52d74fc5d9d3cce Mon Sep 17 00:00:00 2001
From: Tilen Komel
Date: Thu, 30 Jan 2025 18:03:28 +0100
Subject: [PATCH 19/51] Update TimeAgo
---
src/components/generic/TimeAgo.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/generic/TimeAgo.tsx b/src/components/generic/TimeAgo.tsx
index 723ebf19..7507a7a3 100755
--- a/src/components/generic/TimeAgo.tsx
+++ b/src/components/generic/TimeAgo.tsx
@@ -12,7 +12,7 @@ const getTimeAgo = (timestamp: number): string => {
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`;
+ return `${Math.floor(secondsPast / 86400)} days ago ${secondsPast}`;
};
export const TimeAgo = ({ timestamp }: TimeAgoProps): JSX.Element => {
From 7142f0f8d5e4b8465c039a593bd8d5c09033d6a3 Mon Sep 17 00:00:00 2001
From: Tilen Komel
Date: Thu, 30 Jan 2025 18:03:56 +0100
Subject: [PATCH 20/51] Add dialog Node Details
---
src/components/Dialog/DialogManager.tsx | 8 +
src/components/Dialog/NodeDetailsDialog.tsx | 191 ++++++++++++++++++++
src/components/Dialog/NodeOptionsDialog.tsx | 12 +-
src/components/UI/Dialog.tsx | 2 +-
src/core/stores/appStore.ts | 7 +
src/core/stores/deviceStore.ts | 5 +-
6 files changed, 222 insertions(+), 3 deletions(-)
create mode 100644 src/components/Dialog/NodeDetailsDialog.tsx
diff --git a/src/components/Dialog/DialogManager.tsx b/src/components/Dialog/DialogManager.tsx
index e1769751..d571ffcb 100644
--- a/src/components/Dialog/DialogManager.tsx
+++ b/src/components/Dialog/DialogManager.tsx
@@ -6,6 +6,8 @@ import { QRDialog } from "@components/Dialog/QRDialog.tsx";
import { RebootDialog } from "@components/Dialog/RebootDialog.tsx";
import { ShutdownDialog } from "@components/Dialog/ShutdownDialog.tsx";
import { useDevice } from "@core/stores/deviceStore.ts";
+import type { JSX } from "react";
+import { NodeDetailsDialog } from "./NodeDetailsDialog";
export const DialogManager = (): JSX.Element => {
const { channels, config, dialog, setDialogOpen } = useDevice();
@@ -56,6 +58,12 @@ export const DialogManager = (): JSX.Element => {
setDialogOpen("pkiBackup", open);
}}
/>
+ {
+ setDialogOpen("nodeDetails", open);
+ }}
+ />
>
);
};
diff --git a/src/components/Dialog/NodeDetailsDialog.tsx b/src/components/Dialog/NodeDetailsDialog.tsx
new file mode 100644
index 00000000..13a9782d
--- /dev/null
+++ b/src/components/Dialog/NodeDetailsDialog.tsx
@@ -0,0 +1,191 @@
+import { useAppStore } from "@app/core/stores/appStore";
+import { useDevice } from "@app/core/stores/deviceStore";
+import {
+ Dialog,
+ DialogContent,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+} from "@components/UI/Dialog.tsx";
+import { Protobuf } from "@meshtastic/js";
+import { numberToHexUnpadded } from "@noble/curves/abstract/utils";
+import { useEffect } from "react";
+import { useState } from "react";
+import { DeviceImage } from "../generic/DeviceImage";
+import { TimeAgo } from "../generic/TimeAgo";
+import { Uptime } from "../generic/Uptime";
+
+export interface NodeDetailsDialogProps {
+ open: boolean;
+ onOpenChange: (open: boolean) => void;
+}
+
+export const NodeDetailsDialog = ({
+ open,
+ onOpenChange,
+}: NodeDetailsDialogProps) => {
+ const { nodes } = useDevice();
+ const { nodeNumDetails } = useAppStore();
+ const [device, setDevice] = useState(null);
+
+ useEffect(() => {
+ if (!nodeNumDetails) return;
+ setDevice(nodes.get(nodeNumDetails));
+ }, [nodeNumDetails, nodes]);
+
+ return device ? (
+
+ ) : null;
+};
diff --git a/src/components/Dialog/NodeOptionsDialog.tsx b/src/components/Dialog/NodeOptionsDialog.tsx
index 06a3f0b8..83a01426 100644
--- a/src/components/Dialog/NodeOptionsDialog.tsx
+++ b/src/components/Dialog/NodeOptionsDialog.tsx
@@ -25,7 +25,7 @@ export const NodeOptionsDialog = ({
onOpenChange,
}: NodeOptionsDialogProps): JSX.Element => {
const { setDialogOpen, connection } = useDevice();
- const { setNodeNumToBeRemoved } = useAppStore();
+ const { setNodeNumToBeRemoved, setNodeNumDetails } = useAppStore();
const longName =
node?.user?.longName ??
(node ? `!${numberToHexUnpadded(node?.num)}` : "Unknown");
@@ -85,6 +85,16 @@ export const NodeOptionsDialog = ({
Remove
+
+
+
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 21/51] 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 22/51] 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 23/51] Update timeAgo to use Intl.RelativeTimeFormat
---
src/components/generic/TimeAgo.tsx | 47 +++++++++++++++++++-----------
1 file changed, 30 insertions(+), 17 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);
-
- 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 getTimeAgo = (
+ unixTimestamp: number,
+ locale: Intl.LocalesArgument = "en",
+): string => {
+ const timestamp = new Date(unixTimestamp);
+ const diff = (new Date().getTime() - timestamp.getTime()) / 1000;
+
+ 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 24/51] 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 25/51] 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 26/51] 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 27/51] 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 28/51] 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 29/51] 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 30/51] 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 ? (