Browse Source

minor fixes

pull/149/head
Sacha Weatherstone 3 years ago
parent
commit
f7ba8ad967
Failed to extract signature
  1. 12
      src/components/PageComponents/Connect/BLE.tsx
  2. 10
      src/components/PageComponents/Connect/HTTP.tsx
  3. 14
      src/components/PageComponents/Connect/Serial.tsx
  4. 2
      src/components/PageComponents/Messages/MessageInput.tsx
  5. 13
      src/core/hooks/useToast.ts
  6. 6
      src/core/stores/appStore.ts
  7. 85
      src/core/stores/deviceStore.ts
  8. 11
      src/core/subscriptions.ts
  9. 4
      src/core/utils/bitwise.ts
  10. 1
      src/index.tsx
  11. 1
      src/pages/Config/DeviceConfig.tsx
  12. 4
      src/pages/Dashboard/index.tsx
  13. 28
      src/pages/Map.tsx
  14. 10
      src/pages/Messages.tsx

12
src/components/PageComponents/Connect/BLE.tsx

@ -17,16 +17,16 @@ export const BLE = (): JSX.Element => {
}, []);
useEffect(() => {
void updateBleDeviceList();
updateBleDeviceList();
}, [updateBleDeviceList]);
const onConnect = async (BLEDevice: BluetoothDevice) => {
const onConnect = async (bleDevice: BluetoothDevice) => {
const id = randId();
const device = addDevice(id);
setSelectedDevice(id);
const connection = new IBLEConnection(id);
await connection.connect({
device: BLEDevice,
device: bleDevice,
});
device.addConnection(connection);
subscribeAll(device, connection);
@ -39,7 +39,7 @@ export const BLE = (): JSX.Element => {
<Button
key={device.id}
onClick={() => {
void onConnect(device);
onConnect(device);
}}
>
{device.name}
@ -50,8 +50,8 @@ export const BLE = (): JSX.Element => {
)}
</div>
<Button
onClick={() => {
void navigator.bluetooth
onClick={async () => {
await navigator.bluetooth
.requestDevice({
filters: [{ services: [Constants.ServiceUuid] }],
})

10
src/components/PageComponents/Connect/HTTP.tsx

@ -1,7 +1,6 @@
import { Button } from "@components/UI/Button.js";
import { Input } from "@components/UI/Input.js";
import { Label } from "@components/UI/Label.js";
import { SelectLabel } from "@components/UI/Select.js";
import { Switch } from "@components/UI/Switch.js";
import { useAppStore } from "@core/stores/appStore.js";
import { useDeviceStore } from "@core/stores/deviceStore.js";
@ -27,19 +26,19 @@ export const HTTP = (): JSX.Element => {
},
});
const TLSEnabled = useWatch({
const tlsEnabled = useWatch({
control,
name: "tls",
defaultValue: location.protocol === "https:",
});
const onSubmit = handleSubmit((data) => {
const onSubmit = handleSubmit(async (data) => {
const id = randId();
const device = addDevice(id);
setSelectedDevice(id);
const connection = new IHTTPConnection(id);
// TODO: Promise never resolves
void connection.connect({
await connection.connect({
address: data.ip,
fetchInterval: 2000,
tls: data.tls,
@ -49,13 +48,12 @@ export const HTTP = (): JSX.Element => {
});
return (
// eslint-disable-next-line @typescript-eslint/no-misused-promises
<form className="flex w-full flex-col gap-2 p-4" onSubmit={onSubmit}>
<div className="flex h-48 flex-col gap-2">
<Label>IP Address/Hostname</Label>
<Input
// label="IP Address/Hostname"
prefix={TLSEnabled ? "https://" : "http://"}
prefix={tlsEnabled ? "https://" : "http://"}
placeholder="000.000.000.000 / meshtastic.local"
{...register("ip")}
/>

14
src/components/PageComponents/Connect/Serial.tsx

@ -17,13 +17,13 @@ export const Serial = (): JSX.Element => {
}, []);
navigator.serial.addEventListener("connect", () => {
void updateSerialPortList();
updateSerialPortList();
});
navigator.serial.addEventListener("disconnect", () => {
void updateSerialPortList();
updateSerialPortList();
});
useEffect(() => {
void updateSerialPortList();
updateSerialPortList();
}, [updateSerialPortList]);
const onConnect = async (port: SerialPort) => {
@ -49,8 +49,8 @@ export const Serial = (): JSX.Element => {
<Button
key={index}
disabled={port.readable !== null}
onClick={() => {
void onConnect(port);
onClick={async () => {
await onConnect(port);
}}
>
{`# ${index} - ${port.getInfo().usbVendorId ?? "UNK"} - ${
@ -63,8 +63,8 @@ export const Serial = (): JSX.Element => {
)}
</div>
<Button
onClick={() => {
void navigator.serial.requestPort().then((port) => {
onClick={async () => {
await navigator.serial.requestPort().then((port) => {
setSerialPorts(serialPorts.concat(port));
});
}}

2
src/components/PageComponents/Messages/MessageInput.tsx

@ -61,7 +61,7 @@ export const MessageInput = ({
<div className="flex flex-grow gap-2">
<span className="w-full">
<Input
autoFocus
autoFocus={true}
minLength={2}
placeholder="Enter Message"
value={messageDraft}

13
src/core/hooks/useToast.ts

@ -1,4 +1,4 @@
import * as React from "react";
import { useEffect, useState, ReactNode } from "react";
import type { ToastActionElement, ToastProps } from "@components/UI/Toast.js";
@ -7,8 +7,8 @@ const TOAST_REMOVE_DELAY = 1000000;
type ToasterToast = ToastProps & {
id: string;
title?: React.ReactNode;
description?: React.ReactNode;
title?: ReactNode;
description?: ReactNode;
action?: ToastActionElement;
};
@ -109,7 +109,7 @@ export const reducer = (state: State, action: Action): State => {
),
};
}
case "REMOVE_TOAST":
case "REMOVE_TOAST": {
if (action.toastId === undefined) {
return {
...state,
@ -120,6 +120,7 @@ export const reducer = (state: State, action: Action): State => {
...state,
toasts: state.toasts.filter((t) => t.id !== action.toastId),
};
}
}
};
@ -166,9 +167,9 @@ function toast({ ...props }: Toast) {
}
function useToast() {
const [state, setState] = React.useState<State>(memoryState);
const [state, setState] = useState<State>(memoryState);
React.useEffect(() => {
useEffect(() => {
listeners.push(setState);
return () => {
const index = listeners.indexOf(setState);

6
src/core/stores/appStore.ts

@ -8,7 +8,7 @@ export interface RasterSource {
tileSize: number;
}
export type accentColor =
export type AccentColor =
| "red"
| "orange"
| "yellow"
@ -26,7 +26,7 @@ interface AppState {
rasterSources: RasterSource[];
commandPaletteOpen: boolean;
darkMode: boolean;
accent: accentColor;
accent: AccentColor;
connectDialogOpen: boolean;
setRasterSources: (sources: RasterSource[]) => void;
@ -38,7 +38,7 @@ interface AppState {
removeDevice: (deviceId: number) => void;
setCommandPaletteOpen: (open: boolean) => void;
setDarkMode: (enabled: boolean) => void;
setAccent: (color: accentColor) => void;
setAccent: (color: AccentColor) => void;
setConnectDialogOpen: (open: boolean) => void;
}

85
src/core/stores/deviceStore.ts

@ -4,7 +4,6 @@ import { produce } from "immer";
import { create } from "zustand";
import { Protobuf, Types } from "@meshtastic/meshtasticjs";
import { channel } from "diagnostics_channel";
export type Page = "messages" | "map" | "config" | "channels" | "peers";
@ -14,7 +13,7 @@ export interface MessageWithState extends Types.PacketMetadata<string> {
export type MessageState = "ack" | "waiting" | Protobuf.Routing_Error;
export interface processPacketParams {
export interface ProcessPacketParams {
from: number;
snr: number;
time: number;
@ -84,7 +83,7 @@ export interface Device {
state: MessageState,
) => void;
setDialogOpen: (dialog: DialogVariant, open: boolean) => void;
processPacket: (data: processPacketParams) => void;
processPacket: (data: ProcessPacketParams) => void;
setMessageDraft: (message: string) => void;
}
@ -152,27 +151,34 @@ export const useDeviceStore = create<DeviceState>((set, get) => ({
if (device) {
switch (config.payloadVariant.case) {
case "device":
case "device": {
device.config.device = config.payloadVariant.value;
break;
case "position":
}
case "position": {
device.config.position = config.payloadVariant.value;
break;
case "power":
}
case "power": {
device.config.power = config.payloadVariant.value;
break;
case "network":
}
case "network": {
device.config.network = config.payloadVariant.value;
break;
case "display":
}
case "display": {
device.config.display = config.payloadVariant.value;
break;
case "lora":
}
case "lora": {
device.config.lora = config.payloadVariant.value;
break;
case "bluetooth":
}
case "bluetooth": {
device.config.bluetooth = config.payloadVariant.value;
break;
}
}
}
}),
@ -185,43 +191,58 @@ export const useDeviceStore = create<DeviceState>((set, get) => ({
if (device) {
switch (config.payloadVariant.case) {
case "mqtt":
case "mqtt": {
device.moduleConfig.mqtt = config.payloadVariant.value;
break;
case "serial":
}
case "serial": {
device.moduleConfig.serial = config.payloadVariant.value;
break;
case "externalNotification":
}
case "externalNotification": {
device.moduleConfig.externalNotification =
config.payloadVariant.value;
break;
case "storeForward":
}
case "storeForward": {
device.moduleConfig.storeForward =
config.payloadVariant.value;
break;
case "rangeTest":
}
case "rangeTest": {
device.moduleConfig.rangeTest =
config.payloadVariant.value;
break;
case "telemetry":
}
case "telemetry": {
device.moduleConfig.telemetry =
config.payloadVariant.value;
break;
case "cannedMessage":
}
case "cannedMessage": {
device.moduleConfig.cannedMessage =
config.payloadVariant.value;
break;
case "audio":
}
case "audio": {
device.moduleConfig.audio = config.payloadVariant.value;
break;
case "neighborInfo":
device.moduleConfig.neighborInfo = config.payloadVariant.value;
}
case "neighborInfo": {
device.moduleConfig.neighborInfo =
config.payloadVariant.value;
break;
}
case "ambientLighting": {
device.moduleConfig.ambientLighting =
config.payloadVariant.value;
break;
case "ambientLighting":
device.moduleConfig.ambientLighting = config.payloadVariant.value;
}
case "detectionSensor": {
device.moduleConfig.detectionSensor =
config.payloadVariant.value;
break;
case "detectionSensor":
device.moduleConfig.detectionSensor = config.payloadVariant.value;
}
}
}
}),
@ -519,7 +540,7 @@ export const useDeviceStore = create<DeviceState>((set, get) => ({
}),
);
},
processPacket(data: processPacketParams) {
processPacket(data: ProcessPacketParams) {
set(
produce<DeviceState>((draft) => {
const device = draft.devices.get(id);
@ -527,7 +548,13 @@ export const useDeviceStore = create<DeviceState>((set, get) => ({
return;
}
const node = device.nodes.get(data.from);
if (!node) {
if (node) {
device.nodes.set(data.from, {
...node,
lastHeard: data.time,
snr: data.snr,
});
} else {
device.nodes.set(
data.from,
new Protobuf.NodeInfo({
@ -536,12 +563,6 @@ export const useDeviceStore = create<DeviceState>((set, get) => ({
snr: data.snr,
}),
);
} else {
device.nodes.set(data.from, {
...node,
lastHeard: data.time,
snr: data.snr,
});
}
}),
);

11
src/core/subscriptions.ts

@ -16,20 +16,21 @@ export const subscribeAll = (
connection.events.onRoutingPacket.subscribe((routingPacket) => {
switch (routingPacket.data.variant.case) {
case "errorReason":
case "errorReason": {
if (routingPacket.data.variant.value === Protobuf.Routing_Error.NONE) {
return;
}
console.log(`Routing Error: ${routingPacket.data.variant.value}`);
break;
case "routeReply":
}
case "routeReply": {
console.log(`Route Reply: ${routingPacket.data.variant.value}`);
break;
case "routeRequest":
}
case "routeRequest": {
console.log(`Route Request: ${routingPacket.data.variant.value}`);
break;
}
}
});

4
src/core/utils/bitwise.ts

@ -1,4 +1,4 @@
export interface enumLike {
export interface EnumLike {
[key: number]: string | number;
}
@ -8,7 +8,7 @@ export const bitwiseEncode = (enumValues: number[]): number => {
export const bitwiseDecode = (
value: number,
decodeEnum: enumLike,
decodeEnum: EnumLike,
): number[] => {
const enumValues = Object.keys(decodeEnum).map(Number).filter(Boolean);
return enumValues.map((b) => value & b).filter(Boolean);

1
src/index.tsx

@ -6,7 +6,6 @@ import { createRoot } from "react-dom/client";
import { App } from "@app/App.js";
// eslint-disable-next-line @typescript-eslint/non-nullable-type-assertion-style
const container = document.getElementById("root") as HTMLElement;
const root = createRoot(container);

1
src/pages/Config/DeviceConfig.tsx

@ -12,7 +12,6 @@ import {
TabsTrigger,
} from "@components/UI/Tabs.js";
import { useDevice } from "@core/stores/deviceStore.js";
import { Fragment } from "react";
export const DeviceConfig = (): JSX.Element => {
const { metadata } = useDevice();

4
src/pages/Dashboard/index.tsx

@ -6,9 +6,7 @@ import { H3 } from "@components/UI/Typography/H3.js";
import { Subtle } from "@components/UI/Typography/Subtle.js";
import {
BluetoothIcon,
CalendarIcon,
ListPlusIcon,
MapPinIcon,
NetworkIcon,
PlusIcon,
UsbIcon,
@ -35,7 +33,7 @@ export const Dashboard = () => {
<div className="flex h-[450px] rounded-md border border-dashed border-slate-200 p-3 dark:border-slate-700">
{devices.length ? (
<ul role="list" className="grow divide-y divide-gray-200">
<ul className="grow divide-y divide-gray-200">
{devices.map((device) => {
return (
<li key={device.id}>

28
src/pages/Map.tsx

@ -14,9 +14,9 @@ import {
ZoomInIcon,
ZoomOutIcon,
} from "lucide-react";
import { useCallback, useEffect, useState } from "react";
import { Layer, Marker, Source, useMap } from "react-map-gl";
import MapGL from "react-map-gl/maplibre";
import { useEffect, useState } from "react";
import { Marker, useMap } from "react-map-gl";
import MapGl from "react-map-gl/maplibre";
export const MapPage = (): JSX.Element => {
const { nodes, waypoints } = useDevice();
@ -28,11 +28,15 @@ export const MapPage = (): JSX.Element => {
const allNodes = Array.from(nodes.values());
const getBBox = () => {
if (!map) return;
if (!map) {
return;
}
const nodesWithPosition = allNodes.filter(
(node) => node.position?.latitudeI,
);
if (!nodesWithPosition.length) return;
if (!nodesWithPosition.length) {
return;
}
if (nodesWithPosition.length === 1) {
map.easeTo({
zoom: 12,
@ -57,20 +61,22 @@ export const MapPage = (): JSX.Element => {
],
{ padding: { top: 10, bottom: 10, left: 10, right: 10 } },
);
if (center) map.easeTo(center);
if (center) {
map.easeTo(center);
}
};
useEffect(() => {
map?.on("zoom", () => {
setZoom(map?.getZoom() ?? 0);
});
}, [map, zoom]);
}, [map]);
useEffect(() => {
if (map) {
getBBox();
}
}, [map]);
}, [map, getBBox]);
return (
<>
@ -83,7 +89,7 @@ export const MapPage = (): JSX.Element => {
</Sidebar>
<PageLayout
label="Map"
noPadding
noPadding={true}
actions={[
{
icon: ZoomInIcon,
@ -105,7 +111,7 @@ export const MapPage = (): JSX.Element => {
},
]}
>
<MapGL
<MapGl
mapStyle="https://raw.githubusercontent.com/hc-oss/maplibre-gl-styles/master/styles/osm-mapnik/v8/default.json"
// onClick={(e) => {
// const waypoint = new Protobuf.Waypoint({
@ -177,7 +183,7 @@ export const MapPage = (): JSX.Element => {
);
}
})}
</MapGL>
</MapGl>
</PageLayout>
</>
);

10
src/pages/Messages.tsx

@ -8,7 +8,7 @@ import { Hashicon } from "@emeraldpay/hashicon-react";
import { Protobuf, Types } from "@meshtastic/meshtasticjs";
import { getChannelName } from "@pages/Channels.js";
import { HashIcon } from "lucide-react";
import { useMemo, useState } from "react";
import { useState } from "react";
export const MessagesPage = (): JSX.Element => {
const { channels, nodes, hardware, messages } = useDevice();
@ -37,8 +37,8 @@ export const MessagesPage = (): JSX.Element => {
channel.settings?.name.length
? channel.settings?.name
: channel.index === 0
? "Primary"
: `Ch ${channel.index}`
? "Primary"
: `Ch ${channel.index}`
}
active={activeChat === channel.index}
onClick={() => {
@ -69,8 +69,8 @@ export const MessagesPage = (): JSX.Element => {
chatType === "broadcast" && currentChannel
? getChannelName(currentChannel)
: chatType === "direct" && nodes.get(activeChat)
? nodes.get(activeChat)?.user?.longName ?? "Unknown"
: "Loading..."
? nodes.get(activeChat)?.user?.longName ?? "Unknown"
: "Loading..."
}`}
>
{allChannels.map(

Loading…
Cancel
Save