diff --git a/src/components/Card.tsx b/src/components/Card.tsx new file mode 100644 index 00000000..e653756d --- /dev/null +++ b/src/components/Card.tsx @@ -0,0 +1,18 @@ +import type React from "react"; + +export const Card = ({ + children, + className, + ...rest +}: JSX.IntrinsicElements["div"]): JSX.Element => { + return ( +
+ {children} +
+ ); +}; diff --git a/src/components/DeviceSelector.tsx b/src/components/DeviceSelector.tsx index 73090d66..25b78fb0 100644 --- a/src/components/DeviceSelector.tsx +++ b/src/components/DeviceSelector.tsx @@ -5,13 +5,15 @@ import { useDeviceStore } from "@app/core/stores/deviceStore.js"; import { Hashicon } from "@emeraldpay/hashicon-react"; import { PlusIcon } from "@heroicons/react/24/outline"; +import { Mono } from "./Mono.js"; + export const DeviceSelector = (): JSX.Element => { const { getDevices } = useDeviceStore(); const { selectedDevice, setSelectedDevice } = useAppStore(); return ( -
- Connected Devices +
+ Connected Devices {getDevices().map((device) => (
{ return ( @@ -23,7 +24,9 @@ export const IconButton = ({ : "bg-orange-100 text-orange-700 hover:bg-orange-200" } ${ size === "sm" ? "h-8 w-8" : size === "md" ? "h-10 w-10" : "h-12 w-12" - } ${disabled ? "cursor-not-allowed bg-red-400 focus:ring-red-500" : ""}`} + } ${disabled ? "cursor-not-allowed bg-red-400 focus:ring-red-500" : ""} ${ + className ?? "" + }`} disabled={disabled} {...rest} > diff --git a/src/components/Mono.tsx b/src/components/Mono.tsx new file mode 100644 index 00000000..8a0fc422 --- /dev/null +++ b/src/components/Mono.tsx @@ -0,0 +1,16 @@ +import type React from "react"; + +export const Mono = ({ + children, + className, + ...rest +}: JSX.IntrinsicElements["span"]): JSX.Element => { + return ( + + {children} + + ); +}; diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index 255fb469..508dfd35 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -1,10 +1,13 @@ import type React from "react"; +import { useEffect, useState } from "react"; import { useDevice } from "@app/core/providers/useDevice.js"; +import { toMGRS } from "@app/core/utils/toMGRS.js"; import { useAppStore } from "@core/stores/appStore.js"; import { useDeviceStore } from "@core/stores/deviceStore.js"; -import { Types } from "@meshtastic/meshtasticjs"; +import { Protobuf, Types } from "@meshtastic/meshtasticjs"; +import { BatteryWidget } from "./Widgets/BatteryWidget.js"; import { ConfiguringWidget } from "./Widgets/ConfiguringWidget.js"; import { DeviceWidget } from "./Widgets/DeviceWidget.js"; import { NodeInfoWidget } from "./Widgets/NodeInfoWidget.js"; @@ -15,6 +18,21 @@ export const Sidebar = (): JSX.Element => { const { removeDevice } = useDeviceStore(); const { connection, hardware, nodes, status } = useDevice(); const { selectedDevice, setSelectedDevice } = useAppStore(); + const [telemtery, setTelemetry] = useState(); + const [grid, setGrid] = useState(""); + const [batteryHistory, setBatteryHistory] = useState([]); + + useEffect(() => { + const device = nodes.find((n) => n.data.num === hardware.myNodeNum); + if (device?.deviceMetrics?.length) { + setTelemetry(device.deviceMetrics[device.deviceMetrics?.length]); + } + if (device?.data.position) { + setGrid( + toMGRS(device.data.position.latitudeI, device.data.position.longitudeI) + ); + } + }, [nodes, hardware.myNodeNum]); return (
@@ -49,29 +67,14 @@ export const Sidebar = (): JSX.Element => { {/* */} {/* */} {/* */} -
-
-

Information

-
-
-
Firmware version
-
- {hardware.firmwareVersion} -
-
-
-
-
Bitrate
-
- {hardware.bitrate.toFixed(2)} - bps -
-
-
- - {/* */} - - +
+ + + n.data)} /> +
diff --git a/src/components/Widgets/BatteryWidget.tsx b/src/components/Widgets/BatteryWidget.tsx new file mode 100644 index 00000000..3a631004 --- /dev/null +++ b/src/components/Widgets/BatteryWidget.tsx @@ -0,0 +1,30 @@ +import type React from "react"; + +import { BoltIcon } from "@heroicons/react/24/outline"; + +import { Card } from "../Card.js"; + +export interface BatteryWidgetProps { + batteryLevel: number; + voltage: number; +} + +export const BatteryWidget = ({ + batteryLevel, + voltage, +}: BatteryWidgetProps): JSX.Element => { + return ( + +
+ +
+
+
+ Power +
+ {batteryLevel} + {voltage} +
+
+ ); +}; diff --git a/src/components/Widgets/ConfiguringWidget.tsx b/src/components/Widgets/ConfiguringWidget.tsx index 06056428..ba148182 100644 --- a/src/components/Widgets/ConfiguringWidget.tsx +++ b/src/components/Widgets/ConfiguringWidget.tsx @@ -2,6 +2,9 @@ import React, { useEffect } from "react"; import { useDevice } from "@core/providers/useDevice.js"; +import { Button } from "../Button.js"; +import { Card } from "../Card.js"; + export const ConfiguringWidget = (): JSX.Element => { const { hardware, @@ -32,40 +35,45 @@ export const ConfiguringWidget = (): JSX.Element => { ]); return ( -
-

Connecting to device

-
    - - - - - -
-
{ - void connection?.configure(); - }} - > - Retry + +
+ Power +
+
+
    + + + + + +
+
-
+ ); }; diff --git a/src/components/Widgets/DeviceWidget.tsx b/src/components/Widgets/DeviceWidget.tsx index 7b0ce308..8c414bfb 100644 --- a/src/components/Widgets/DeviceWidget.tsx +++ b/src/components/Widgets/DeviceWidget.tsx @@ -4,6 +4,7 @@ import { Hashicon } from "@emeraldpay/hashicon-react"; import { XCircleIcon } from "@heroicons/react/24/outline"; import { Button } from "../Button.js"; +import { Card } from "../Card.js"; export interface DeviceWidgetProps { name: string; @@ -21,7 +22,7 @@ export const DeviceWidget = ({ reconnect, }: DeviceWidgetProps): JSX.Element => { return ( -
+
@@ -45,6 +46,6 @@ export const DeviceWidget = ({
-
+ ); }; diff --git a/src/components/Widgets/NodeInfoWidget.tsx b/src/components/Widgets/NodeInfoWidget.tsx index 5dfbc5cf..f3e376a3 100644 --- a/src/components/Widgets/NodeInfoWidget.tsx +++ b/src/components/Widgets/NodeInfoWidget.tsx @@ -1,11 +1,38 @@ import type React from "react"; -export interface NodeInfoWidgetProps {} +import type { Protobuf } from "@meshtastic/meshtasticjs"; -export const NodeInfoWidget = ({}: NodeInfoWidgetProps): JSX.Element => { +import { Card } from "../Card.js"; + +export interface NodeInfoWidgetProps { + hardware: Protobuf.MyNodeInfo; +} + +export const NodeInfoWidget = ({ + hardware, +}: NodeInfoWidgetProps): JSX.Element => { return ( -
- node info -
+ +
+ Information +
+
+
+
+
Firmware version
+
+ {hardware.firmwareVersion} +
+
+
+
+
Bitrate
+
+ {hardware.bitrate.toFixed(2)} + bps +
+
+
+
); }; diff --git a/src/components/Widgets/PeersWidget.tsx b/src/components/Widgets/PeersWidget.tsx index 990a18b9..0b7af404 100644 --- a/src/components/Widgets/PeersWidget.tsx +++ b/src/components/Widgets/PeersWidget.tsx @@ -1,11 +1,55 @@ import type React from "react"; -export interface PeersWidgetProps {} +import { base16 } from "rfc4648"; -export const PeersWidget = ({}: PeersWidgetProps): JSX.Element => { +import { Hashicon } from "@emeraldpay/hashicon-react"; +import { EllipsisHorizontalCircleIcon } from "@heroicons/react/24/outline"; +import type { Protobuf } from "@meshtastic/meshtasticjs"; + +import { Card } from "../Card.js"; +import { IconButton } from "../IconButton.js"; +import { Mono } from "../Mono.js"; + +export interface PeersWidgetProps { + peers: Protobuf.NodeInfo[]; +} + +export const PeersWidget = ({ peers }: PeersWidgetProps): JSX.Element => { return ( -
- Peers -
+ +
+
+ Peers +
+
+ {peers.map((peer) => ( +
+ + + +
+ {peer.user?.longName} + + {base16 + .stringify(peer.user?.macaddr ?? []) + .match(/.{1,2}/g) + ?.join(":") ?? ""} + +
+
+ } + /> +
+
+ ))} +
+
+
); }; diff --git a/src/components/Widgets/PositionWidget.tsx b/src/components/Widgets/PositionWidget.tsx index e384b8fb..9dd520e2 100644 --- a/src/components/Widgets/PositionWidget.tsx +++ b/src/components/Widgets/PositionWidget.tsx @@ -1,11 +1,25 @@ import type React from "react"; -export interface PositionWidgetProps {} +import { MapPinIcon } from "@heroicons/react/24/outline"; -export const PositionWidget = ({}: PositionWidgetProps): JSX.Element => { +import { Card } from "../Card.js"; + +export interface PositionWidgetProps { + grid: string; +} + +export const PositionWidget = ({ grid }: PositionWidgetProps): JSX.Element => { return ( -
- position -
+ +
+ +
+
+
+ Position +
+ {grid} +
+
); };