From ed9ee28745a7f74e76e0d034993b0f2ff5aa3d14 Mon Sep 17 00:00:00 2001 From: Sacha Weatherstone Date: Tue, 9 Aug 2022 08:33:54 +1000 Subject: [PATCH] WIP updates --- package.json | 4 +- pnpm-lock.yaml | 42 +++--- .../PageComponents/Config/Display.tsx | 19 ++- .../PageComponents/Config/WiFi copy.tsx | 94 ------------- src/components/PageComponents/Config/WiFi.tsx | 69 +++++----- src/components/layout/Sidebar/index.tsx | 2 + src/core/stores/deviceStore.ts | 72 ++++++---- src/core/subscriptions.ts | 14 +- src/pages/Info/index.tsx | 5 +- src/pages/Messages/LocationMessage.tsx | 33 +++++ src/pages/Messages/Message.tsx | 130 ++++++++---------- src/validation/config/display.ts | 5 +- src/validation/config/wifi.ts | 14 +- 13 files changed, 239 insertions(+), 264 deletions(-) delete mode 100644 src/components/PageComponents/Config/WiFi copy.tsx create mode 100644 src/pages/Messages/LocationMessage.tsx diff --git a/package.json b/package.json index 99f8418f..be8c8bdf 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "@emeraldpay/hashicon-react": "^0.5.2", "@hookform/resolvers": "^2.9.7", "@meshtastic/eslint-config": "^1.0.8", - "@meshtastic/meshtasticjs": "^0.6.84", + "@meshtastic/meshtasticjs": "^0.6.87", "base64-js": "^1.5.1", "class-transformer": "^0.5.1", "class-validator": "^0.13.2", @@ -49,7 +49,7 @@ "@types/chrome": "^0.0.193", "@types/geodesy": "^2.2.3", "@types/node": "^18.6.4", - "@types/react": "^18.0.15", + "@types/react": "^18.0.16", "@types/react-dom": "^18.0.6", "@types/w3c-web-serial": "^1.0.2", "@types/web-bluetooth": "^0.0.15", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0c6a1508..6f5c2e50 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,11 +5,11 @@ specifiers: '@emeraldpay/hashicon-react': ^0.5.2 '@hookform/resolvers': ^2.9.7 '@meshtastic/eslint-config': ^1.0.8 - '@meshtastic/meshtasticjs': ^0.6.84 + '@meshtastic/meshtasticjs': ^0.6.87 '@types/chrome': ^0.0.193 '@types/geodesy': ^2.2.3 '@types/node': ^18.6.4 - '@types/react': ^18.0.15 + '@types/react': ^18.0.16 '@types/react-dom': ^18.0.6 '@types/w3c-web-serial': ^1.0.2 '@types/web-bluetooth': ^0.0.15 @@ -47,7 +47,7 @@ dependencies: '@emeraldpay/hashicon-react': 0.5.2 '@hookform/resolvers': 2.9.7_react-hook-form@7.34.0 '@meshtastic/eslint-config': 1.0.8 - '@meshtastic/meshtasticjs': 0.6.84 + '@meshtastic/meshtasticjs': 0.6.87 base64-js: 1.5.1 class-transformer: 0.5.1 class-validator: 0.13.2 @@ -72,7 +72,7 @@ devDependencies: '@types/chrome': 0.0.193 '@types/geodesy': 2.2.3 '@types/node': 18.6.4 - '@types/react': 18.0.15 + '@types/react': 18.0.16 '@types/react-dom': 18.0.6 '@types/w3c-web-serial': 1.0.2 '@types/web-bluetooth': 0.0.15 @@ -584,8 +584,8 @@ packages: - supports-color dev: false - /@meshtastic/meshtasticjs/0.6.84: - resolution: {integrity: sha512-ydylXdjfTzxLh1sapDpSlkpkFR2izq9bOLP8KH4eV/4bmogk+Uc/bizLR6qJ0jQyUVcl2wFJcnOHyYwKz0jCzA==} + /@meshtastic/meshtasticjs/0.6.87: + resolution: {integrity: sha512-vehcqwhHMlUm9C/UnhmmhwVZzBjFTkYSrerwbAzJrvLC4E6tRIHCgeyPYtJZaqJoZ90gE+PD+haH2sc86mLgZQ==} dependencies: '@meshtastic/eslint-config': 1.0.8 '@protobuf-ts/runtime': 2.7.0 @@ -836,25 +836,25 @@ packages: /@types/react-dom/18.0.6: resolution: {integrity: sha512-/5OFZgfIPSwy+YuIBP/FgJnQnsxhZhjjrnxudMddeblOouIodEQ75X14Rr4wGSG/bknL+Omy9iWlLo1u/9GzAA==} dependencies: - '@types/react': 18.0.15 + '@types/react': 18.0.16 dev: true /@types/react-transition-group/4.4.5: resolution: {integrity: sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA==} dependencies: - '@types/react': 18.0.15 + '@types/react': 18.0.16 dev: false - /@types/react/16.14.29: - resolution: {integrity: sha512-I5IwEaefGZbpmmK1J7zHwZe3JkGxcRkc5WJUDWmNySVVovueViRTEUWV7spTvpe96l3nbKD/K6+GxoN69CYb/w==} + /@types/react/16.14.30: + resolution: {integrity: sha512-tG+xGtDDSuIl1l63mN0LnaROAc99knkYyN4YTheE80iPzYvSy0U8LVie+OBZkrgjVrpkQV6bMCkSphPBnVNk6g==} dependencies: '@types/prop-types': 15.7.5 '@types/scheduler': 0.16.2 csstype: 3.1.0 dev: false - /@types/react/18.0.15: - resolution: {integrity: sha512-iz3BtLuIYH1uWdsv6wXYdhozhqj20oD4/Hk2DNXIn1kFsmp9x8d9QB6FnPhfkbhd2PgEONt9Q1x/ebkwjfFLow==} + /@types/react/18.0.16: + resolution: {integrity: sha512-3vX1dzVucqc2nhXtzyaParTIIRZeNbisRqLE7QdeLomVybEyeiuAouzZXgz71P+2kbJOqj3dy0fzoATg2I06GQ==} dependencies: '@types/prop-types': 15.7.5 '@types/scheduler': 0.16.2 @@ -1421,7 +1421,7 @@ packages: isarray: 2.0.5 object-is: 1.1.5 object-keys: 1.1.1 - object.assign: 4.1.2 + object.assign: 4.1.3 regexp.prototype.flags: 1.4.3 side-channel: 1.0.4 which-boxed-primitive: 1.0.2 @@ -1556,7 +1556,7 @@ packages: is-weakref: 1.0.2 object-inspect: 1.12.2 object-keys: 1.1.1 - object.assign: 4.1.2 + object.assign: 4.1.3 regexp.prototype.flags: 1.4.3 string.prototype.trimend: 1.0.5 string.prototype.trimstart: 1.0.5 @@ -2131,7 +2131,7 @@ packages: dependencies: '@babel/runtime': 7.18.9 '@segment/react-tiny-virtual-list': 2.2.1_react@18.2.0 - '@types/react': 16.14.29 + '@types/react': 16.14.30 '@types/react-transition-group': 4.4.5 arrify: 1.0.1 classnames: 2.3.1 @@ -2768,7 +2768,7 @@ packages: engines: {node: '>=4.0'} dependencies: array-includes: 3.1.5 - object.assign: 4.1.2 + object.assign: 4.1.3 dev: false /levn/0.4.1: @@ -3009,8 +3009,8 @@ packages: resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} engines: {node: '>= 0.4'} - /object.assign/4.1.2: - resolution: {integrity: sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==} + /object.assign/4.1.3: + resolution: {integrity: sha512-ZFJnX3zltyjcYJL0RoCJuzb+11zWGyaDbjgxZbdV7rFEcHQuYxrZqhow67aA7xpes6LhojyFDaBKAFfogQrikA==} engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 @@ -3218,8 +3218,8 @@ packages: node-modules-regexp: 1.0.0 dev: true - /postcss/8.4.14: - resolution: {integrity: sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==} + /postcss/8.4.16: + resolution: {integrity: sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==} engines: {node: ^10 || ^12 || >=14} dependencies: nanoid: 3.3.4 @@ -4055,7 +4055,7 @@ packages: optional: true dependencies: esbuild: 0.14.53 - postcss: 8.4.14 + postcss: 8.4.16 resolve: 1.22.1 rollup: 2.77.2 optionalDependencies: diff --git a/src/components/PageComponents/Config/Display.tsx b/src/components/PageComponents/Config/Display.tsx index e0971cae..bcc8a0d1 100644 --- a/src/components/PageComponents/Config/Display.tsx +++ b/src/components/PageComponents/Config/Display.tsx @@ -1,8 +1,8 @@ import type React from "react"; import { useEffect, useState } from "react"; -import { SelectField, TextInputField } from "evergreen-ui"; -import { useForm } from "react-hook-form"; +import { FormField, SelectField, Switch, TextInputField } from "evergreen-ui"; +import { Controller, useForm } from "react-hook-form"; import { DisplayValidation } from "@app/validation/config/display.js"; import { Form } from "@components/form/Form"; @@ -19,6 +19,7 @@ export const Display = (): JSX.Element => { handleSubmit, formState: { errors, isDirty }, reset, + control, } = useForm({ defaultValues: config.display, resolver: classValidatorResolver(DisplayValidation), @@ -67,6 +68,20 @@ export const Display = (): JSX.Element => { > {renderOptions(Protobuf.Config_DisplayConfig_GpsCoordinateFormat)} + + ( + + )} + /> + ); }; diff --git a/src/components/PageComponents/Config/WiFi copy.tsx b/src/components/PageComponents/Config/WiFi copy.tsx deleted file mode 100644 index c3446017..00000000 --- a/src/components/PageComponents/Config/WiFi copy.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import type React from "react"; -import { useEffect, useState } from "react"; - -import { FormField, Switch, TextInputField, toaster } from "evergreen-ui"; -import { Controller, useForm } from "react-hook-form"; - -import { WiFiValidation } from "@app/validation/config/wifi.js"; -import { Form } from "@components/form/Form"; -import { useDevice } from "@core/stores/deviceStore.js"; -import { classValidatorResolver } from "@hookform/resolvers/class-validator"; - -export const WiFi = (): JSX.Element => { - const { config, connection } = useDevice(); - const [loading, setLoading] = useState(false); - const { - register, - handleSubmit, - formState: { errors, isDirty }, - control, - reset, - } = useForm({ - defaultValues: config.wifi, - resolver: classValidatorResolver(WiFiValidation), - }); - - useEffect(() => { - reset(config.wifi); - }, [reset, config.wifi]); - - const onSubmit = handleSubmit((data) => { - setLoading(true); - void connection?.setConfig( - { - payloadVariant: { - oneofKind: "wifi", - wifi: data, - }, - }, - async () => { - toaster.success("Your source is now sending data"); - reset({ ...data }); - setLoading(false); - await Promise.resolve(); - } - ); - }); - return ( -
- - - - ( - - )} - /> - - - ( - - )} - /> - - - ); -}; diff --git a/src/components/PageComponents/Config/WiFi.tsx b/src/components/PageComponents/Config/WiFi.tsx index ccc8ea13..f3379cdd 100644 --- a/src/components/PageComponents/Config/WiFi.tsx +++ b/src/components/PageComponents/Config/WiFi.tsx @@ -1,13 +1,21 @@ import type React from "react"; import { useEffect, useState } from "react"; -import { FormField, Switch, TextInputField, toaster } from "evergreen-ui"; -import { Controller, useForm } from "react-hook-form"; +import { + FormField, + SelectField, + Switch, + TextInputField, + toaster, +} from "evergreen-ui"; +import { Controller, useForm, useWatch } from "react-hook-form"; +import { renderOptions } from "@app/core/utils/selectEnumOptions.js"; import { WiFiValidation } from "@app/validation/config/wifi.js"; import { Form } from "@components/form/Form"; import { useDevice } from "@core/stores/deviceStore.js"; import { classValidatorResolver } from "@hookform/resolvers/class-validator"; +import { Protobuf } from "@meshtastic/meshtasticjs"; export const WiFi = (): JSX.Element => { const { config, connection } = useDevice(); @@ -23,6 +31,12 @@ export const WiFi = (): JSX.Element => { resolver: classValidatorResolver(WiFiValidation), }); + const wifiEnabled = useWatch({ + control, + name: "enabled", + defaultValue: false, + }); + useEffect(() => { reset(config.wifi); }, [reset, config.wifi]); @@ -46,6 +60,27 @@ export const WiFi = (): JSX.Element => { }); return (
+ + ( + + )} + /> + + + {renderOptions(Protobuf.Config_WiFiConfig_WiFiMode)} + { validationMessage={errors.ssid?.message} {...register("ssid")} /> - { validationMessage={errors.psk?.message} {...register("psk")} /> - - ( - - )} - /> - - - - ( - - )} - /> - ); }; diff --git a/src/components/layout/Sidebar/index.tsx b/src/components/layout/Sidebar/index.tsx index 1d9482d4..ccdb317e 100644 --- a/src/components/layout/Sidebar/index.tsx +++ b/src/components/layout/Sidebar/index.tsx @@ -82,6 +82,7 @@ export const Sidebar = (): JSX.Element => { {navLinks.map((Link) => ( { ))} void; @@ -50,8 +51,9 @@ export interface Device { setModuleConfig: (config: Protobuf.ModuleConfig) => void; setHardware: (hardware: Protobuf.MyNodeInfo) => void; setMetrics: (metrics: Types.TelemetryPacket) => void; - setActiveChat: (chatIndex: number) => void; setActivePage: (page: Page) => void; + setPeerInfoOpen: (open: boolean) => void; + setActivePeer: (peer: number) => void; addNodeInfo: (nodeInfo: Types.NodeInfoPacket) => void; addUser: (user: Types.UserPacket) => void; addPosition: (position: Types.PositionPacket) => void; @@ -85,8 +87,9 @@ export const useDeviceStore = create((set, get) => ({ hardware: Protobuf.MyNodeInfo.create(), nodes: [], connection: undefined, - activeChat: 0, activePage: "messages", + peerInfoOpen: false, + activePeer: 0, setReady: (ready: boolean) => { set( @@ -219,8 +222,8 @@ export const useDeviceStore = create((set, get) => ({ snr: metrics.packet.rxSnr, lastHeard: new Date().getSeconds(), }), - deviceMetrics: Protobuf.DeviceMetrics.create(), - environmentMetrics: Protobuf.EnvironmentMetrics.create(), + deviceMetrics: [], + environmentMetrics: [], }; device.nodes.push(node); @@ -228,27 +231,20 @@ export const useDeviceStore = create((set, get) => ({ if (node) { switch (metrics.data.variant.oneofKind) { case "deviceMetrics": - node.deviceMetrics = metrics.data.variant.deviceMetrics; + node.deviceMetrics.push( + metrics.data.variant.deviceMetrics + ); break; case "environmentMetrics": - node.environmentMetrics = - metrics.data.variant.environmentMetrics; + node.environmentMetrics.push( + metrics.data.variant.environmentMetrics + ); break; } } }) ); }, - setActiveChat: (chatIndex) => { - set( - produce((draft) => { - const device = draft.devices.get(id); - if (device) { - device.activeChat = chatIndex; - } - }) - ); - }, setActivePage: (page) => { set( produce((draft) => { @@ -277,14 +273,34 @@ export const useDeviceStore = create((set, get) => ({ } else { device.nodes.push({ data: Protobuf.NodeInfo.create(nodeInfo.data), - deviceMetrics: Protobuf.DeviceMetrics.create(), - environmentMetrics: Protobuf.EnvironmentMetrics.create(), + deviceMetrics: [], + environmentMetrics: [], }); } } }) ); }, + setPeerInfoOpen: (open) => { + set( + produce((draft) => { + const device = draft.devices.get(id); + if (device) { + device.peerInfoOpen = open; + } + }) + ); + }, + setActivePeer: (peer) => { + set( + produce((draft) => { + const device = draft.devices.get(id); + if (device) { + device.activePeer = peer; + } + }) + ); + }, addUser: (user) => { set( produce((draft) => { @@ -307,8 +323,8 @@ export const useDeviceStore = create((set, get) => ({ snr: user.packet.rxSnr, user: user.data, }), - deviceMetrics: Protobuf.DeviceMetrics.create(), - environmentMetrics: Protobuf.EnvironmentMetrics.create(), + deviceMetrics: [], + environmentMetrics: [], }); } } @@ -336,8 +352,8 @@ export const useDeviceStore = create((set, get) => ({ num: position.packet.from, position: position.data, }), - deviceMetrics: Protobuf.DeviceMetrics.create(), - environmentMetrics: Protobuf.EnvironmentMetrics.create(), + deviceMetrics: [], + environmentMetrics: [], }); } } @@ -369,6 +385,8 @@ export const useDeviceStore = create((set, get) => ({ ); }, ackMessage: (channelIndex: number, messageId: number) => { + console.log("ack called"); + set( produce((draft) => { const device = draft.devices.get(id); diff --git a/src/core/subscriptions.ts b/src/core/subscriptions.ts index 163dbee1..0ee1af33 100644 --- a/src/core/subscriptions.ts +++ b/src/core/subscriptions.ts @@ -12,14 +12,14 @@ export const subscribeAll = (device: Device, connection: IConnection) => { // onLogEvent // onMeshHeartbeat - // onRoutingPacket - // onTelemetryPacket connection.onRoutingPacket.subscribe((routingPacket) => { console.log(routingPacket); }); connection.onTelemetryPacket.subscribe((telemetryPacket) => { + console.log(telemetryPacket.data.variant); + device.setMetrics(telemetryPacket); }); @@ -63,12 +63,12 @@ export const subscribeAll = (device: Device, connection: IConnection) => { device.setModuleConfig(moduleConfig.data); }); - connection.onTextPacket.subscribe((message) => { + connection.onMessagePacket.subscribe((messagePacket) => { device.addMessage({ - message: message, - ack: message.packet.from !== device.hardware.myNodeNum, - received: message.packet.rxTime - ? new Date(message.packet.rxTime * 1000) + message: messagePacket, + ack: messagePacket.packet.from !== device.hardware.myNodeNum, + received: messagePacket.packet.rxTime + ? new Date(messagePacket.packet.rxTime * 1000) : new Date(), }); }); diff --git a/src/pages/Info/index.tsx b/src/pages/Info/index.tsx index 06ce1466..90347a5b 100644 --- a/src/pages/Info/index.tsx +++ b/src/pages/Info/index.tsx @@ -1,6 +1,7 @@ import type React from "react"; import { Pane } from "evergreen-ui"; +import JSONPretty from "react-json-pretty"; import { useDevice } from "@app/core/stores/deviceStore.js"; @@ -11,8 +12,8 @@ export const InfoPage = (): JSX.Element => { return ( - {JSON.stringify(myNode)} - {JSON.stringify(hardware)} + + ); }; diff --git a/src/pages/Messages/LocationMessage.tsx b/src/pages/Messages/LocationMessage.tsx new file mode 100644 index 00000000..f9f43384 --- /dev/null +++ b/src/pages/Messages/LocationMessage.tsx @@ -0,0 +1,33 @@ +import type React from "react"; + +import { LocateIcon, majorScale, minorScale, Pane, Text } from "evergreen-ui"; + +import { toMGRS } from "@app/core/utils/toMGRS.js"; +import type { Protobuf } from "@meshtastic/meshtasticjs"; + +export interface LocationMessageProps { + location: Protobuf.Location; +} + +export const LocationMessage = ({ + location, +}: LocationMessageProps): JSX.Element => { + return ( + + + + {toMGRS(location.latitudeI, location.longitudeI)} + + + ); +}; diff --git a/src/pages/Messages/Message.tsx b/src/pages/Messages/Message.tsx index 8dcb76be..02f513fc 100644 --- a/src/pages/Messages/Message.tsx +++ b/src/pages/Messages/Message.tsx @@ -1,21 +1,23 @@ import type React from "react"; import { + CircleIcon, + FullCircleIcon, majorScale, Pane, - Pulsar, Small, Strong, Text, - TimeIcon, } from "evergreen-ui"; import { Hashicon } from "@emeraldpay/hashicon-react"; -import type { Protobuf } from "@meshtastic/meshtasticjs"; +import type { Protobuf, Types } from "@meshtastic/meshtasticjs"; + +import { LocationMessage } from "./LocationMessage.js"; export interface MessageProps { lastMsgSameUser: boolean; - message: string; + messagePacket: Types.MessagePacket; ack: boolean; rxTime: Date; sender?: Protobuf.NodeInfo; @@ -23,78 +25,66 @@ export interface MessageProps { export const Message = ({ lastMsgSameUser, - message, + messagePacket, ack, rxTime, sender, }: MessageProps): JSX.Element => { - return ( - - {lastMsgSameUser ? ( - - - - {rxTime - .toLocaleTimeString(undefined, { - hour: "2-digit", - minute: "2-digit", - }) - .replace("AM", "") - .replace("PM", "")} - - - {message} - - - - - - 25s - - + return lastMsgSameUser ? ( + + {ack ? ( + ) : ( - - - - - - - - {sender?.user?.longName ?? "UNK"} - - - {rxTime.toLocaleTimeString(undefined, { - hour: "2-digit", - minute: "2-digit", - })} - - - {message} - - + )} + {messagePacket.location ? ( + + ) : ( + + {messagePacket.text} + + )} + + ) : ( + + + + + + + {sender?.user?.longName ?? "UNK"} + + + {rxTime.toLocaleTimeString(undefined, { + hour: "2-digit", + minute: "2-digit", + })} + + + + {ack ? ( + + ) : ( + + )} + {messagePacket.location ? ( + + ) : ( + + {messagePacket.text} + + )} + ); }; diff --git a/src/validation/config/display.ts b/src/validation/config/display.ts index cfbbbe7f..c81c9c07 100644 --- a/src/validation/config/display.ts +++ b/src/validation/config/display.ts @@ -1,4 +1,4 @@ -import { IsEnum, IsInt } from "class-validator"; +import { IsBoolean, IsEnum, IsInt } from "class-validator"; import { Protobuf } from "@meshtastic/meshtasticjs"; @@ -11,4 +11,7 @@ export class DisplayValidation implements Protobuf.Config_DisplayConfig { @IsInt() autoScreenCarouselSecs: number; + + @IsBoolean() + compassNorthTop: boolean; } diff --git a/src/validation/config/wifi.ts b/src/validation/config/wifi.ts index 6cb938cc..b3039afe 100644 --- a/src/validation/config/wifi.ts +++ b/src/validation/config/wifi.ts @@ -1,15 +1,17 @@ -import { Length } from "class-validator"; +import { IsBoolean, IsEnum, Length } from "class-validator"; -import type { Protobuf } from "@meshtastic/meshtasticjs"; +import { Protobuf } from "@meshtastic/meshtasticjs"; export class WiFiValidation implements Protobuf.Config_WiFiConfig { + @IsBoolean() + enabled: boolean; + + @IsEnum(Protobuf.Config_WiFiConfig_WiFiMode) + mode: Protobuf.Config_WiFiConfig_WiFiMode; + @Length(1, 33) ssid: string; @Length(8, 64) psk: string; - - apMode: boolean; - - apHidden: boolean; }