+
{suffix}
)}
diff --git a/src/core/stores/appStore.ts b/src/core/stores/appStore.ts
index fa46cb57..2098c17b 100644
--- a/src/core/stores/appStore.ts
+++ b/src/core/stores/appStore.ts
@@ -26,6 +26,7 @@ interface AppState {
rasterSources: RasterSource[];
commandPaletteOpen: boolean;
darkMode: boolean;
+ nodeNumToBeRemoved: number;
accent: AccentColor;
connectDialogOpen: boolean;
@@ -38,6 +39,7 @@ interface AppState {
removeDevice: (deviceId: number) => void;
setCommandPaletteOpen: (open: boolean) => void;
setDarkMode: (enabled: boolean) => void;
+ setNodeNumToBeRemoved: (nodeNum: number) => void;
setAccent: (color: AccentColor) => void;
setConnectDialogOpen: (open: boolean) => void;
}
@@ -51,6 +53,7 @@ export const useAppStore = create
()((set) => ({
darkMode: window.matchMedia("(prefers-color-scheme: dark)").matches,
accent: "orange",
connectDialogOpen: false,
+ nodeNumToBeRemoved: 0,
setRasterSources: (sources: RasterSource[]) => {
set(
@@ -99,6 +102,10 @@ export const useAppStore = create()((set) => ({
}),
);
},
+ setNodeNumToBeRemoved: (nodeNum) =>
+ set((state) => ({
+ nodeNumToBeRemoved: nodeNum
+ })),
setAccent(color) {
set(
produce((draft) => {
diff --git a/src/core/stores/deviceStore.ts b/src/core/stores/deviceStore.ts
index fee9fb8f..bfd927fc 100644
--- a/src/core/stores/deviceStore.ts
+++ b/src/core/stores/deviceStore.ts
@@ -24,7 +24,8 @@ export type DialogVariant =
| "QR"
| "shutdown"
| "reboot"
- | "deviceName";
+ | "deviceName"
+ | "nodeRemoval";
export interface Device {
id: number;
@@ -54,6 +55,7 @@ export interface Device {
shutdown: boolean;
reboot: boolean;
deviceName: boolean;
+ nodeRemoval: boolean;
};
setStatus: (status: Types.DeviceStatusEnum) => void;
@@ -74,6 +76,7 @@ export interface Device {
addConnection: (connection: Types.ConnectionType) => void;
addMessage: (message: MessageWithState) => void;
addMetadata: (from: number, metadata: Protobuf.Mesh.DeviceMetadata) => void;
+ removeNode: (nodeNum: number) => void;
setMessageState: (
type: "direct" | "broadcast",
channelIndex: Types.ChannelNumber,
@@ -130,6 +133,7 @@ export const useDeviceStore = create((set, get) => ({
shutdown: false,
reboot: false,
deviceName: false,
+ nodeRemoval: false,
},
pendingSettingsChanges: false,
messageDraft: "",
@@ -494,6 +498,17 @@ export const useDeviceStore = create((set, get) => ({
}),
);
},
+ removeNode: (nodeNum) => {
+ set(
+ produce((draft) => {
+ const device = draft.devices.get(id);
+ if (!device) {
+ return;
+ }
+ device.nodes.delete(nodeNum);
+ })
+ )
+ },
setMessageState: (
type: "direct" | "broadcast",
channelIndex: Types.ChannelNumber,
diff --git a/src/pages/Nodes.tsx b/src/pages/Nodes.tsx
index 8b1c03a6..f9a46cb0 100644
--- a/src/pages/Nodes.tsx
+++ b/src/pages/Nodes.tsx
@@ -6,9 +6,19 @@ import { useDevice } from "@core/stores/deviceStore.js";
import { Hashicon } from "@emeraldpay/hashicon-react";
import { Protobuf } from "@meshtastic/js";
import { base16 } from "rfc4648";
+import { Button } from "@components/UI/Button.js";
+import { TrashIcon } from "lucide-react";
+import { useAppStore } from "@app/core/stores/appStore";
+
+
+export interface DeleteNoteDialogProps {
+ open: boolean;
+ onOpenChange: (open: boolean) => void;
+}
export const NodesPage = (): JSX.Element => {
- const { nodes, hardware } = useDevice();
+ const { nodes, hardware, setDialogOpen } = useDevice();
+ const { setNodeNumToBeRemoved } = useAppStore();
const filteredNodes = Array.from(nodes.values()).filter(
(n) => n.num !== hardware.myNodeNum,
@@ -26,6 +36,8 @@ export const NodesPage = (): JSX.Element => {
{ title: "MAC Address", type: "normal", sortable: true },
{ title: "Last Heard", type: "normal", sortable: true },
{ title: "SNR", type: "normal", sortable: true },
+ { title: "Connection", type: "normal", sortable: true },
+ { title: "Remove", type: "normal", sortable: false },
]}
rows={filteredNodes.map((node) => [
,
@@ -55,6 +67,17 @@ export const NodesPage = (): JSX.Element => {
{Math.min(Math.max((node.snr + 10) * 5, 0), 100)}%/
{(node.snr + 10) * 5}raw
,
+
+ {node.lastHeard != 0 ?
+ (node.viaMqtt === false && node.hopsAway === 0
+ ? "Direct": node.hopsAway.toString() + " hops away")
+ : "-"}
+ {node.viaMqtt === true? ", via MQTT": ""}
+ ,
+
])}
/>
diff --git a/src/validation/channel.ts b/src/validation/channel.ts
index 336e35e1..33349d06 100644
--- a/src/validation/channel.ts
+++ b/src/validation/channel.ts
@@ -41,4 +41,13 @@ export class Channel_SettingsValidation
@IsBoolean()
downlinkEnabled: boolean;
+
+ @IsBoolean()
+ positionEnabled: boolean;
+
+ @IsBoolean()
+ preciseLocation: boolean;
+
+ @IsInt()
+ positionPrecision: number;
}
diff --git a/src/validation/config/position.ts b/src/validation/config/position.ts
index 2f035744..df991961 100644
--- a/src/validation/config/position.ts
+++ b/src/validation/config/position.ts
@@ -7,6 +7,12 @@ const DeprecatedPositionValidationFields = ['gpsEnabled', 'gpsAttemptTime'];
export class PositionValidation
implements Omit