WIP changes
This commit is contained in:
+2
-2
@@ -48,7 +48,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/chrome": "^0.0.193",
|
"@types/chrome": "^0.0.193",
|
||||||
"@types/geodesy": "^2.2.3",
|
"@types/geodesy": "^2.2.3",
|
||||||
"@types/node": "^18.6.5",
|
"@types/node": "^18.7.1",
|
||||||
"@types/react": "^18.0.17",
|
"@types/react": "^18.0.17",
|
||||||
"@types/react-dom": "^18.0.6",
|
"@types/react-dom": "^18.0.6",
|
||||||
"@types/w3c-web-serial": "^1.0.2",
|
"@types/w3c-web-serial": "^1.0.2",
|
||||||
@@ -60,7 +60,7 @@
|
|||||||
"tslib": "^2.4.0",
|
"tslib": "^2.4.0",
|
||||||
"typescript": "^4.7.4",
|
"typescript": "^4.7.4",
|
||||||
"unimported": "^1.21.0",
|
"unimported": "^1.21.0",
|
||||||
"vite": "^3.0.4",
|
"vite": "^3.0.5",
|
||||||
"vite-plugin-cdn-import": "^0.3.5"
|
"vite-plugin-cdn-import": "^0.3.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+31
-27
@@ -8,7 +8,7 @@ specifiers:
|
|||||||
'@meshtastic/meshtasticjs': ^0.6.88
|
'@meshtastic/meshtasticjs': ^0.6.88
|
||||||
'@types/chrome': ^0.0.193
|
'@types/chrome': ^0.0.193
|
||||||
'@types/geodesy': ^2.2.3
|
'@types/geodesy': ^2.2.3
|
||||||
'@types/node': ^18.6.5
|
'@types/node': ^18.7.1
|
||||||
'@types/react': ^18.0.17
|
'@types/react': ^18.0.17
|
||||||
'@types/react-dom': ^18.0.6
|
'@types/react-dom': ^18.0.6
|
||||||
'@types/w3c-web-serial': ^1.0.2
|
'@types/w3c-web-serial': ^1.0.2
|
||||||
@@ -37,7 +37,7 @@ specifiers:
|
|||||||
tslib: ^2.4.0
|
tslib: ^2.4.0
|
||||||
typescript: ^4.7.4
|
typescript: ^4.7.4
|
||||||
unimported: ^1.21.0
|
unimported: ^1.21.0
|
||||||
vite: ^3.0.4
|
vite: ^3.0.5
|
||||||
vite-plugin-cdn-import: ^0.3.5
|
vite-plugin-cdn-import: ^0.3.5
|
||||||
vite-plugin-environment: ^1.1.2
|
vite-plugin-environment: ^1.1.2
|
||||||
zustand: 4.0.0
|
zustand: 4.0.0
|
||||||
@@ -65,25 +65,25 @@ dependencies:
|
|||||||
rfc4648: 1.5.2
|
rfc4648: 1.5.2
|
||||||
snarkdown: 2.0.0
|
snarkdown: 2.0.0
|
||||||
swr: 1.3.0_react@18.2.0
|
swr: 1.3.0_react@18.2.0
|
||||||
vite-plugin-environment: 1.1.2_vite@3.0.4
|
vite-plugin-environment: 1.1.2_vite@3.0.5
|
||||||
zustand: 4.0.0_immer@9.0.15+react@18.2.0
|
zustand: 4.0.0_immer@9.0.15+react@18.2.0
|
||||||
|
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@types/chrome': 0.0.193
|
'@types/chrome': 0.0.193
|
||||||
'@types/geodesy': 2.2.3
|
'@types/geodesy': 2.2.3
|
||||||
'@types/node': 18.6.5
|
'@types/node': 18.7.1
|
||||||
'@types/react': 18.0.17
|
'@types/react': 18.0.17
|
||||||
'@types/react-dom': 18.0.6
|
'@types/react-dom': 18.0.6
|
||||||
'@types/w3c-web-serial': 1.0.2
|
'@types/w3c-web-serial': 1.0.2
|
||||||
'@types/web-bluetooth': 0.0.15
|
'@types/web-bluetooth': 0.0.15
|
||||||
'@vitejs/plugin-react': 2.0.0_vite@3.0.4
|
'@vitejs/plugin-react': 2.0.0_vite@3.0.5
|
||||||
gzipper: 7.1.0
|
gzipper: 7.1.0
|
||||||
rollup-plugin-visualizer: 5.7.1
|
rollup-plugin-visualizer: 5.7.1
|
||||||
tar: 6.1.11
|
tar: 6.1.11
|
||||||
tslib: 2.4.0
|
tslib: 2.4.0
|
||||||
typescript: 4.7.4
|
typescript: 4.7.4
|
||||||
unimported: 1.21.0
|
unimported: 1.21.0
|
||||||
vite: 3.0.4
|
vite: 3.0.5
|
||||||
vite-plugin-cdn-import: 0.3.5
|
vite-plugin-cdn-import: 0.3.5
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
@@ -106,7 +106,7 @@ packages:
|
|||||||
'@esri/arcgis-html-sanitizer': 2.10.0
|
'@esri/arcgis-html-sanitizer': 2.10.0
|
||||||
'@esri/calcite-colors': 6.0.1
|
'@esri/calcite-colors': 6.0.1
|
||||||
'@esri/calcite-components': 1.0.0-beta.82
|
'@esri/calcite-components': 1.0.0-beta.82
|
||||||
'@popperjs/core': 2.11.5
|
'@popperjs/core': 2.11.6
|
||||||
focus-trap: 6.9.4
|
focus-trap: 6.9.4
|
||||||
luxon: 2.4.0
|
luxon: 2.4.0
|
||||||
sortablejs: 1.15.0
|
sortablejs: 1.15.0
|
||||||
@@ -620,6 +620,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-9X2obfABZuDVLCgPK9aX0a/x4jaOEweTTWE2+9sr0Qqqevj2Uv5XorvusThmc9XGYpS9yI+fhh8RTafBtGposw==}
|
resolution: {integrity: sha512-9X2obfABZuDVLCgPK9aX0a/x4jaOEweTTWE2+9sr0Qqqevj2Uv5XorvusThmc9XGYpS9yI+fhh8RTafBtGposw==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@popperjs/core/2.11.6:
|
||||||
|
resolution: {integrity: sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@protobuf-ts/runtime/2.7.0:
|
/@protobuf-ts/runtime/2.7.0:
|
||||||
resolution: {integrity: sha512-CzunTplg81oMkF9MnSYiX2WOdHnAyHKnokZncgOHm5bt8iEfFNnUsMSbsWUus/ulRNIZ9VAVMjutd96vmySQaw==}
|
resolution: {integrity: sha512-CzunTplg81oMkF9MnSYiX2WOdHnAyHKnokZncgOHm5bt8iEfFNnUsMSbsWUus/ulRNIZ9VAVMjutd96vmySQaw==}
|
||||||
dev: false
|
dev: false
|
||||||
@@ -822,8 +826,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==}
|
resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@types/node/18.6.5:
|
/@types/node/18.7.1:
|
||||||
resolution: {integrity: sha512-Xjt5ZGUa5WusGZJ4WJPbOT8QOqp6nDynVFRKcUt32bOgvXEoc6o085WNkYTMO7ifAj2isEfQQ2cseE+wT6jsRw==}
|
resolution: {integrity: sha512-GKX1Qnqxo4S+Z/+Z8KKPLpH282LD7jLHWJcVryOflnsnH+BtSDfieR6ObwBMwpnNws0bUK8GI7z0unQf9bARNQ==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@types/normalize-package-data/2.4.1:
|
/@types/normalize-package-data/2.4.1:
|
||||||
@@ -1013,7 +1017,7 @@ packages:
|
|||||||
'@typescript-eslint/types': 5.33.0
|
'@typescript-eslint/types': 5.33.0
|
||||||
eslint-visitor-keys: 3.3.0
|
eslint-visitor-keys: 3.3.0
|
||||||
|
|
||||||
/@vitejs/plugin-react/2.0.0_vite@3.0.4:
|
/@vitejs/plugin-react/2.0.0_vite@3.0.5:
|
||||||
resolution: {integrity: sha512-zHkRR+X4zqEPNBbKV2FvWSxK7Q6crjMBVIAYroSU8Nbb4M3E5x4qOiLoqJBHtXgr27kfednXjkwr3lr8jS6Wrw==}
|
resolution: {integrity: sha512-zHkRR+X4zqEPNBbKV2FvWSxK7Q6crjMBVIAYroSU8Nbb4M3E5x4qOiLoqJBHtXgr27kfednXjkwr3lr8jS6Wrw==}
|
||||||
engines: {node: '>=14.18.0'}
|
engines: {node: '>=14.18.0'}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -1026,7 +1030,7 @@ packages:
|
|||||||
'@babel/plugin-transform-react-jsx-source': 7.18.6_@babel+core@7.18.10
|
'@babel/plugin-transform-react-jsx-source': 7.18.6_@babel+core@7.18.10
|
||||||
magic-string: 0.26.2
|
magic-string: 0.26.2
|
||||||
react-refresh: 0.14.0
|
react-refresh: 0.14.0
|
||||||
vite: 3.0.4
|
vite: 3.0.5
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
@@ -1197,8 +1201,8 @@ packages:
|
|||||||
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
|
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
dependencies:
|
dependencies:
|
||||||
caniuse-lite: 1.0.30001374
|
caniuse-lite: 1.0.30001375
|
||||||
electron-to-chromium: 1.4.212
|
electron-to-chromium: 1.4.215
|
||||||
node-releases: 2.0.6
|
node-releases: 2.0.6
|
||||||
update-browserslist-db: 1.0.5_browserslist@4.21.3
|
update-browserslist-db: 1.0.5_browserslist@4.21.3
|
||||||
dev: true
|
dev: true
|
||||||
@@ -1224,8 +1228,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
|
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
/caniuse-lite/1.0.30001374:
|
/caniuse-lite/1.0.30001375:
|
||||||
resolution: {integrity: sha512-mWvzatRx3w+j5wx/mpFN5v5twlPrabG8NqX2c6e45LCpymdoGqNvRkRutFUqpRTXKFQFNQJasvK0YT7suW6/Hw==}
|
resolution: {integrity: sha512-kWIMkNzLYxSvnjy0hL8w1NOaWNr2rn39RTAVyIwcw8juu60bZDWiF1/loOYANzjtJmy6qPgNmn38ro5Pygagdw==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/chalk/2.4.2:
|
/chalk/2.4.2:
|
||||||
@@ -1256,7 +1260,7 @@ packages:
|
|||||||
/class-validator/0.13.2:
|
/class-validator/0.13.2:
|
||||||
resolution: {integrity: sha512-yBUcQy07FPlGzUjoLuUfIOXzgynnQPPruyK1Ge2B74k9ROwnle1E+NxLWnUv5OLU8hA/qL5leAE9XnXq3byaBw==}
|
resolution: {integrity: sha512-yBUcQy07FPlGzUjoLuUfIOXzgynnQPPruyK1Ge2B74k9ROwnle1E+NxLWnUv5OLU8hA/qL5leAE9XnXq3byaBw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
libphonenumber-js: 1.10.11
|
libphonenumber-js: 1.10.12
|
||||||
validator: 13.7.0
|
validator: 13.7.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
@@ -1501,8 +1505,8 @@ packages:
|
|||||||
stream-shift: 1.0.1
|
stream-shift: 1.0.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/electron-to-chromium/1.4.212:
|
/electron-to-chromium/1.4.215:
|
||||||
resolution: {integrity: sha512-LjQUg1SpLj2GfyaPDVBUHdhmlDU1vDB4f0mJWSGkISoXQrn5/lH3ECPCuo2Bkvf6Y30wO+b69te+rZK/llZmjg==}
|
resolution: {integrity: sha512-vqZxT8C5mlDZ//hQFhneHmOLnj1LhbzxV0+I1yqHV8SB1Oo4Y5Ne9+qQhwHl7O1s9s9cRuo2l5CoLEHdhMTwZg==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/emoji-regex/8.0.0:
|
/emoji-regex/8.0.0:
|
||||||
@@ -2778,8 +2782,8 @@ packages:
|
|||||||
prelude-ls: 1.2.1
|
prelude-ls: 1.2.1
|
||||||
type-check: 0.4.0
|
type-check: 0.4.0
|
||||||
|
|
||||||
/libphonenumber-js/1.10.11:
|
/libphonenumber-js/1.10.12:
|
||||||
resolution: {integrity: sha512-ehoihx4HpRXO6FH/uJ0EnaEV4dVU+FDny+jv0S6k9JPyPsAIr0eXDAFvGRMBKE1daCtyHAaFSKCiuCxrOjVAzQ==}
|
resolution: {integrity: sha512-xTFBs3ipFQNmjCUkDj6ZzRJvs97IyazFHBKWtrQrLiYs0Zk0GANob1hkMRlQUQXbJrpQGwnI+/yU4oyD4ohvpw==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/lines-and-columns/1.2.4:
|
/lines-and-columns/1.2.4:
|
||||||
@@ -3517,8 +3521,8 @@ packages:
|
|||||||
yargs: 17.5.1
|
yargs: 17.5.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/rollup/2.77.2:
|
/rollup/2.77.3:
|
||||||
resolution: {integrity: sha512-m/4YzYgLcpMQbxX3NmAqDvwLATZzxt8bIegO78FZLl+lAgKJBd1DRAOeEiZcKOIOPjxE6ewHWHNgGEalFXuz1g==}
|
resolution: {integrity: sha512-/qxNTG7FbmefJWoeeYJFbHehJ2HNWnjkAFRKzWN/45eNBBF/r8lo992CwcJXEzyVxs5FmfId+vTSTQDb+bxA+g==}
|
||||||
engines: {node: '>=10.0.0'}
|
engines: {node: '>=10.0.0'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
@@ -4027,16 +4031,16 @@ packages:
|
|||||||
- rollup
|
- rollup
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/vite-plugin-environment/1.1.2_vite@3.0.4:
|
/vite-plugin-environment/1.1.2_vite@3.0.5:
|
||||||
resolution: {integrity: sha512-WFgM/ibceOEIuficZVaLcmJvcMZiyTkGzeS8+pzfByGYRdewqil7LSLDV1DwJfFQIx/YzcW9YRSWQG7cJ2XT1w==}
|
resolution: {integrity: sha512-WFgM/ibceOEIuficZVaLcmJvcMZiyTkGzeS8+pzfByGYRdewqil7LSLDV1DwJfFQIx/YzcW9YRSWQG7cJ2XT1w==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
vite: '>= 2.7'
|
vite: '>= 2.7'
|
||||||
dependencies:
|
dependencies:
|
||||||
vite: 3.0.4
|
vite: 3.0.5
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/vite/3.0.4:
|
/vite/3.0.5:
|
||||||
resolution: {integrity: sha512-NU304nqnBeOx2MkQnskBQxVsa0pRAH5FphokTGmyy8M3oxbvw7qAXts2GORxs+h/2vKsD+osMhZ7An6yK6F1dA==}
|
resolution: {integrity: sha512-bRvrt9Tw8EGW4jj64aYFTnVg134E8hgDxyl/eEHnxiGqYk7/pTPss6CWlurqPOUzqvEoZkZ58Ws+Iu8MB87iMA==}
|
||||||
engines: {node: ^14.18.0 || >=16.0.0}
|
engines: {node: ^14.18.0 || >=16.0.0}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -4057,7 +4061,7 @@ packages:
|
|||||||
esbuild: 0.14.54
|
esbuild: 0.14.54
|
||||||
postcss: 8.4.16
|
postcss: 8.4.16
|
||||||
resolve: 1.22.1
|
resolve: 1.22.1
|
||||||
rollup: 2.77.2
|
rollup: 2.77.3
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
fsevents: 2.3.2
|
fsevents: 2.3.2
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,8 @@ export const PeersDialog = ({
|
|||||||
isOpen,
|
isOpen,
|
||||||
close,
|
close,
|
||||||
}: PeersDialogProps): JSX.Element => {
|
}: PeersDialogProps): JSX.Element => {
|
||||||
const { hardware, nodes, connection } = useDevice();
|
const { hardware, nodes, connection, setPeerInfoOpen, setActivePeer } =
|
||||||
|
useDevice();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog
|
<Dialog
|
||||||
@@ -48,6 +49,7 @@ export const PeersDialog = ({
|
|||||||
SNR
|
SNR
|
||||||
</Table.TextHeaderCell>
|
</Table.TextHeaderCell>
|
||||||
<Table.TextHeaderCell>Location</Table.TextHeaderCell>
|
<Table.TextHeaderCell>Location</Table.TextHeaderCell>
|
||||||
|
<Table.TextHeaderCell>Telemetry</Table.TextHeaderCell>
|
||||||
<Table.TextHeaderCell>Last Heard</Table.TextHeaderCell>
|
<Table.TextHeaderCell>Last Heard</Table.TextHeaderCell>
|
||||||
<Table.TextHeaderCell>Actions</Table.TextHeaderCell>
|
<Table.TextHeaderCell>Actions</Table.TextHeaderCell>
|
||||||
</Table.Head>
|
</Table.Head>
|
||||||
@@ -58,7 +60,10 @@ export const PeersDialog = ({
|
|||||||
<Table.Row
|
<Table.Row
|
||||||
key={node.data.num}
|
key={node.data.num}
|
||||||
isSelectable
|
isSelectable
|
||||||
onSelect={() => alert(node.data.num)}
|
onSelect={() => {
|
||||||
|
setActivePeer(node.data.num);
|
||||||
|
setPeerInfoOpen(true);
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Table.Cell flexBasis={48} flexShrink={0} flexGrow={0}>
|
<Table.Cell flexBasis={48} flexShrink={0} flexGrow={0}>
|
||||||
<Hashicon
|
<Hashicon
|
||||||
@@ -81,6 +86,7 @@ export const PeersDialog = ({
|
|||||||
node.data.position?.longitudeI
|
node.data.position?.longitudeI
|
||||||
)}
|
)}
|
||||||
</Table.TextCell>
|
</Table.TextCell>
|
||||||
|
<Table.TextCell>Tmp</Table.TextCell>
|
||||||
<Table.TextCell>
|
<Table.TextCell>
|
||||||
{new Date(node.data.lastHeard * 1000).toLocaleString()}
|
{new Date(node.data.lastHeard * 1000).toLocaleString()}
|
||||||
</Table.TextCell>
|
</Table.TextCell>
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
import type React from "react";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
import { GeolocationIcon, Pane, PropertyIcon, SideSheet } from "evergreen-ui";
|
||||||
|
|
||||||
|
import { Node, useDevice } from "@app/core/stores/deviceStore.js";
|
||||||
|
import { Hashicon } from "@emeraldpay/hashicon-react";
|
||||||
|
import { Protobuf } from "@meshtastic/meshtasticjs";
|
||||||
|
|
||||||
|
import { SlideSheetTabbedContent } from "../layout/page/SlideSheetTabbedContent.js";
|
||||||
|
import type { TabType } from "../layout/page/TabbedContent.js";
|
||||||
|
import { Location } from "./tabs/nodes/Location.js";
|
||||||
|
import { Overview } from "./tabs/nodes/Overview.js";
|
||||||
|
|
||||||
|
export const PeerInfo = () => {
|
||||||
|
const { peerInfoOpen, activePeer, setPeerInfoOpen, nodes } = useDevice();
|
||||||
|
const [selectedTab, setSelectedTab] = useState(0);
|
||||||
|
const [node, setNode] = useState<Node | undefined>();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setNode(nodes.find((n) => n.data.num === activePeer));
|
||||||
|
}, [nodes, activePeer]);
|
||||||
|
|
||||||
|
const tabs: TabType[] = [
|
||||||
|
{
|
||||||
|
name: "Info",
|
||||||
|
icon: PropertyIcon,
|
||||||
|
element: () => <Overview node={node} />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Location",
|
||||||
|
icon: GeolocationIcon,
|
||||||
|
element: () => <Location node={node} />,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SideSheet
|
||||||
|
isShown={peerInfoOpen}
|
||||||
|
onCloseComplete={() => {
|
||||||
|
setPeerInfoOpen(false);
|
||||||
|
}}
|
||||||
|
containerProps={{
|
||||||
|
display: "flex",
|
||||||
|
flex: "1",
|
||||||
|
flexDirection: "column",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SlideSheetTabbedContent
|
||||||
|
heading={node?.data.user?.longName ?? "UNK"}
|
||||||
|
description={Protobuf.HardwareModel[node?.data.user?.hwModel ?? 0]}
|
||||||
|
tabs={tabs}
|
||||||
|
tabIcon={
|
||||||
|
<Pane marginY="auto">
|
||||||
|
<Hashicon size={32} value={(node?.data.num ?? 0).toString()} />
|
||||||
|
</Pane>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</SideSheet>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -4,29 +4,23 @@ import { useCallback, useEffect, useState } from "react";
|
|||||||
import { Button, majorScale, Pane } from "evergreen-ui";
|
import { Button, majorScale, Pane } from "evergreen-ui";
|
||||||
import { FiPlusCircle } from "react-icons/fi";
|
import { FiPlusCircle } from "react-icons/fi";
|
||||||
|
|
||||||
|
import { useAppStore } from "@app/core/stores/appStore.js";
|
||||||
import { useDeviceStore } from "@app/core/stores/deviceStore.js";
|
import { useDeviceStore } from "@app/core/stores/deviceStore.js";
|
||||||
import { subscribeAll } from "@app/core/subscriptions.js";
|
import { subscribeAll } from "@app/core/subscriptions.js";
|
||||||
import { randId } from "@app/core/utils/randId.js";
|
import { randId } from "@app/core/utils/randId.js";
|
||||||
import { Constants, IBLEConnection } from "@meshtastic/meshtasticjs";
|
import { Constants, IBLEConnection } from "@meshtastic/meshtasticjs";
|
||||||
|
|
||||||
import type { CloseProps } from "../SlideSheets/NewDevice.js";
|
import type { CloseProps } from "../../NewDevice.js";
|
||||||
|
|
||||||
export const BLE = ({ close }: CloseProps): JSX.Element => {
|
export const BLE = ({ close }: CloseProps): JSX.Element => {
|
||||||
const [bleDevices, setBleDevices] = useState<BluetoothDevice[]>([]);
|
const [bleDevices, setBleDevices] = useState<BluetoothDevice[]>([]);
|
||||||
const { addDevice } = useDeviceStore();
|
const { addDevice } = useDeviceStore();
|
||||||
|
const { setSelectedDevice } = useAppStore();
|
||||||
|
|
||||||
const updateBleDeviceList = useCallback(async (): Promise<void> => {
|
const updateBleDeviceList = useCallback(async (): Promise<void> => {
|
||||||
setBleDevices(await navigator.bluetooth.getDevices());
|
setBleDevices(await navigator.bluetooth.getDevices());
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
navigator.bluetooth.addEventListener("advertisementreceived", (e) => {
|
|
||||||
console.log(e);
|
|
||||||
});
|
|
||||||
|
|
||||||
navigator.bluetooth.addEventListener("availabilitychanged", (e) => {
|
|
||||||
console.log(e);
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
void updateBleDeviceList();
|
void updateBleDeviceList();
|
||||||
}, [updateBleDeviceList]);
|
}, [updateBleDeviceList]);
|
||||||
@@ -34,6 +28,7 @@ export const BLE = ({ close }: CloseProps): JSX.Element => {
|
|||||||
const onConnect = async (BLEDevice: BluetoothDevice) => {
|
const onConnect = async (BLEDevice: BluetoothDevice) => {
|
||||||
const id = randId();
|
const id = randId();
|
||||||
const device = addDevice(id);
|
const device = addDevice(id);
|
||||||
|
setSelectedDevice(id);
|
||||||
const connection = new IBLEConnection(id);
|
const connection = new IBLEConnection(id);
|
||||||
await connection.connect({
|
await connection.connect({
|
||||||
device: BLEDevice,
|
device: BLEDevice,
|
||||||
@@ -4,6 +4,7 @@ import { Button, majorScale, Pane, TextInputField } from "evergreen-ui";
|
|||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { FiPlusCircle } from "react-icons/fi";
|
import { FiPlusCircle } from "react-icons/fi";
|
||||||
|
|
||||||
|
import { useAppStore } from "@app/core/stores/appStore.js";
|
||||||
import { useDeviceStore } from "@app/core/stores/deviceStore.js";
|
import { useDeviceStore } from "@app/core/stores/deviceStore.js";
|
||||||
import { subscribeAll } from "@app/core/subscriptions.js";
|
import { subscribeAll } from "@app/core/subscriptions.js";
|
||||||
import { randId } from "@app/core/utils/randId.js";
|
import { randId } from "@app/core/utils/randId.js";
|
||||||
@@ -15,6 +16,7 @@ export interface HTTPProps {
|
|||||||
|
|
||||||
export const HTTP = ({ close }: HTTPProps): JSX.Element => {
|
export const HTTP = ({ close }: HTTPProps): JSX.Element => {
|
||||||
const { addDevice } = useDeviceStore();
|
const { addDevice } = useDeviceStore();
|
||||||
|
const { setSelectedDevice } = useAppStore();
|
||||||
const { register, handleSubmit } = useForm<{
|
const { register, handleSubmit } = useForm<{
|
||||||
ip: string;
|
ip: string;
|
||||||
tls: boolean;
|
tls: boolean;
|
||||||
@@ -25,9 +27,10 @@ export const HTTP = ({ close }: HTTPProps): JSX.Element => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const onSubmit = handleSubmit(async (data) => {
|
const onSubmit = handleSubmit((data) => {
|
||||||
const id = randId();
|
const id = randId();
|
||||||
const device = addDevice(id);
|
const device = addDevice(id);
|
||||||
|
setSelectedDevice(id);
|
||||||
const connection = new IHTTPConnection(id);
|
const connection = new IHTTPConnection(id);
|
||||||
// TODO: Promise never resolves
|
// TODO: Promise never resolves
|
||||||
void connection.connect({
|
void connection.connect({
|
||||||
+4
-1
@@ -4,12 +4,13 @@ import { useCallback, useEffect, useState } from "react";
|
|||||||
import { Button, majorScale, Pane } from "evergreen-ui";
|
import { Button, majorScale, Pane } from "evergreen-ui";
|
||||||
import { FiPlusCircle } from "react-icons/fi";
|
import { FiPlusCircle } from "react-icons/fi";
|
||||||
|
|
||||||
|
import { useAppStore } from "@app/core/stores/appStore.js";
|
||||||
import { subscribeAll } from "@app/core/subscriptions.js";
|
import { subscribeAll } from "@app/core/subscriptions.js";
|
||||||
import { useDeviceStore } from "@core/stores/deviceStore.js";
|
import { useDeviceStore } from "@core/stores/deviceStore.js";
|
||||||
import { randId } from "@core/utils/randId.js";
|
import { randId } from "@core/utils/randId.js";
|
||||||
import { ISerialConnection } from "@meshtastic/meshtasticjs";
|
import { ISerialConnection } from "@meshtastic/meshtasticjs";
|
||||||
|
|
||||||
import type { CloseProps } from "../SlideSheets/NewDevice.js";
|
import type { CloseProps } from "../../NewDevice.js";
|
||||||
|
|
||||||
interface USBID {
|
interface USBID {
|
||||||
id: number;
|
id: number;
|
||||||
@@ -19,6 +20,7 @@ interface USBID {
|
|||||||
export const Serial = ({ close }: CloseProps): JSX.Element => {
|
export const Serial = ({ close }: CloseProps): JSX.Element => {
|
||||||
const [serialPorts, setSerialPorts] = useState<SerialPort[]>([]);
|
const [serialPorts, setSerialPorts] = useState<SerialPort[]>([]);
|
||||||
const { addDevice } = useDeviceStore();
|
const { addDevice } = useDeviceStore();
|
||||||
|
const { setSelectedDevice } = useAppStore();
|
||||||
|
|
||||||
const updateSerialPortList = useCallback(async () => {
|
const updateSerialPortList = useCallback(async () => {
|
||||||
setSerialPorts(await navigator.serial.getPorts());
|
setSerialPorts(await navigator.serial.getPorts());
|
||||||
@@ -37,6 +39,7 @@ export const Serial = ({ close }: CloseProps): JSX.Element => {
|
|||||||
const onConnect = async (port: SerialPort) => {
|
const onConnect = async (port: SerialPort) => {
|
||||||
const id = randId();
|
const id = randId();
|
||||||
const device = addDevice(id);
|
const device = addDevice(id);
|
||||||
|
setSelectedDevice(id);
|
||||||
const connection = new ISerialConnection(id);
|
const connection = new ISerialConnection(id);
|
||||||
await connection.connect({
|
await connection.connect({
|
||||||
port,
|
port,
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import type React from "react";
|
||||||
|
|
||||||
|
import { Pane } from "evergreen-ui";
|
||||||
|
import JSONPretty from "react-json-pretty";
|
||||||
|
|
||||||
|
import type { Node } from "@app/core/stores/deviceStore.js";
|
||||||
|
|
||||||
|
export interface LocationProps {
|
||||||
|
node?: Node;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Location = ({ node }: LocationProps): JSX.Element => {
|
||||||
|
return (
|
||||||
|
<Pane>
|
||||||
|
<JSONPretty data={node.data.position} />
|
||||||
|
</Pane>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import type React from "react";
|
||||||
|
|
||||||
|
import { Pane } from "evergreen-ui";
|
||||||
|
import JSONPretty from "react-json-pretty";
|
||||||
|
|
||||||
|
import type { Node } from "@app/core/stores/deviceStore.js";
|
||||||
|
|
||||||
|
export interface OverviewProps {
|
||||||
|
node?: Node;
|
||||||
|
}
|
||||||
|
export const Overview = ({ node }: OverviewProps): JSX.Element => {
|
||||||
|
return (
|
||||||
|
<Pane>
|
||||||
|
<JSONPretty data={node.data.user} />
|
||||||
|
</Pane>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -8,6 +8,7 @@ import { useDeviceStore } from "@core/stores/deviceStore.js";
|
|||||||
|
|
||||||
import { NoDevice } from "../misc/NoDevice.js";
|
import { NoDevice } from "../misc/NoDevice.js";
|
||||||
import { Progress } from "../Progress.js";
|
import { Progress } from "../Progress.js";
|
||||||
|
import { PeerInfo } from "../SlideSheets/PeerInfo.js";
|
||||||
import { Header } from "./Header.js";
|
import { Header } from "./Header.js";
|
||||||
import { Sidebar } from "./Sidebar/index.js";
|
import { Sidebar } from "./Sidebar/index.js";
|
||||||
|
|
||||||
@@ -32,12 +33,12 @@ export const AppLayout = ({ children }: AppLayoutProps): JSX.Element => {
|
|||||||
<Header />
|
<Header />
|
||||||
<Pane display="flex" flex={1} height="100%" width="100%">
|
<Pane display="flex" flex={1} height="100%" width="100%">
|
||||||
{devices.length ? (
|
{devices.length ? (
|
||||||
devices.map((device, index) => (
|
devices.map((device) => (
|
||||||
<Pane
|
<Pane
|
||||||
key={index}
|
key={device.id}
|
||||||
width="100%"
|
width="100%"
|
||||||
height="100%"
|
height="100%"
|
||||||
display={index === selectedDevice ? "grid" : "none"}
|
display={device.id === selectedDevice ? "grid" : "none"}
|
||||||
gap={majorScale(3)}
|
gap={majorScale(3)}
|
||||||
gridTemplateColumns="16rem 1fr"
|
gridTemplateColumns="16rem 1fr"
|
||||||
>
|
>
|
||||||
@@ -45,6 +46,7 @@ export const AppLayout = ({ children }: AppLayoutProps): JSX.Element => {
|
|||||||
{device && device.ready ? (
|
{device && device.ready ? (
|
||||||
<>
|
<>
|
||||||
<Sidebar />
|
<Sidebar />
|
||||||
|
<PeerInfo />
|
||||||
<Pane height="100%" display="flex">
|
<Pane height="100%" display="flex">
|
||||||
{children}
|
{children}
|
||||||
</Pane>
|
</Pane>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
Button,
|
Button,
|
||||||
CrossIcon,
|
CrossIcon,
|
||||||
GlobeIcon,
|
GlobeIcon,
|
||||||
|
HelpIcon,
|
||||||
IconButton,
|
IconButton,
|
||||||
Link,
|
Link,
|
||||||
majorScale,
|
majorScale,
|
||||||
@@ -65,13 +66,13 @@ export const Header = (): JSX.Element => {
|
|||||||
</Link>
|
</Link>
|
||||||
</Pane>
|
</Pane>
|
||||||
<Tablist display="flex" marginX={majorScale(4)}>
|
<Tablist display="flex" marginX={majorScale(4)}>
|
||||||
{getDevices().map((device, index) => (
|
{getDevices().map((device) => (
|
||||||
<Tab
|
<Tab
|
||||||
key={index}
|
key={device.id}
|
||||||
gap={majorScale(1)}
|
gap={majorScale(1)}
|
||||||
isSelected={index === selectedDevice}
|
isSelected={device.id === selectedDevice}
|
||||||
onSelect={() => {
|
onSelect={() => {
|
||||||
setSelectedDevice(index);
|
setSelectedDevice(device.id);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Hashicon value={device.hardware.myNodeNum.toString()} size={20} />
|
<Hashicon value={device.hardware.myNodeNum.toString()} size={20} />
|
||||||
@@ -121,6 +122,9 @@ export const Header = (): JSX.Element => {
|
|||||||
<Button
|
<Button
|
||||||
iconBefore={CrossIcon}
|
iconBefore={CrossIcon}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
void getDevices()
|
||||||
|
.find((d) => d.id === selectedDevice)
|
||||||
|
?.connection?.disconnect();
|
||||||
removeDevice(selectedDevice ?? 0);
|
removeDevice(selectedDevice ?? 0);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -138,6 +142,7 @@ export const Header = (): JSX.Element => {
|
|||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
<IconButton icon={HelpIcon} />
|
||||||
<Tooltip content="Visit Meshtastic.org">
|
<Tooltip content="Visit Meshtastic.org">
|
||||||
<Link target="_blank" href="https://meshtastic.org/">
|
<Link target="_blank" href="https://meshtastic.org/">
|
||||||
<IconButton icon={GlobeIcon} />
|
<IconButton icon={GlobeIcon} />
|
||||||
|
|||||||
@@ -23,24 +23,34 @@ export interface SlideSheetTabbedContentProps {
|
|||||||
heading: string;
|
heading: string;
|
||||||
description: string;
|
description: string;
|
||||||
tabs: TabType[];
|
tabs: TabType[];
|
||||||
|
tabIcon?: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SlideSheetTabbedContent = ({
|
export const SlideSheetTabbedContent = ({
|
||||||
heading,
|
heading,
|
||||||
description,
|
description,
|
||||||
tabs,
|
tabs,
|
||||||
|
tabIcon,
|
||||||
}: SlideSheetTabbedContentProps): JSX.Element => {
|
}: SlideSheetTabbedContentProps): JSX.Element => {
|
||||||
const [selectedTab, setSelectedTab] = useState(0);
|
const [selectedTab, setSelectedTab] = useState(0);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Pane zIndex={1} flexShrink={0} elevation={1} backgroundColor="white">
|
<Pane zIndex={1} flexShrink={0} elevation={1} backgroundColor="white">
|
||||||
<Pane padding={16} borderBottom="muted">
|
<Pane
|
||||||
|
display="flex"
|
||||||
|
padding={16}
|
||||||
|
borderBottom="muted"
|
||||||
|
gap={majorScale(1)}
|
||||||
|
>
|
||||||
|
{tabIcon}
|
||||||
|
<Pane>
|
||||||
<Heading size={600}>{heading}</Heading>
|
<Heading size={600}>{heading}</Heading>
|
||||||
<Paragraph size={400} color="muted">
|
<Paragraph size={400} color="muted">
|
||||||
{description}
|
{description}
|
||||||
</Paragraph>
|
</Paragraph>
|
||||||
</Pane>
|
</Pane>
|
||||||
|
</Pane>
|
||||||
<Pane display="flex" padding={8}>
|
<Pane display="flex" padding={8}>
|
||||||
<Tablist>
|
<Tablist>
|
||||||
{tabs.map((Entry, index) => (
|
{tabs.map((Entry, index) => (
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ export interface Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface Device {
|
export interface Device {
|
||||||
|
id: number;
|
||||||
ready: boolean;
|
ready: boolean;
|
||||||
status: Types.DeviceStatusEnum;
|
status: Types.DeviceStatusEnum;
|
||||||
channels: Channel[];
|
channels: Channel[];
|
||||||
@@ -79,6 +80,7 @@ export const useDeviceStore = create<DeviceState>((set, get) => ({
|
|||||||
set(
|
set(
|
||||||
produce<DeviceState>((draft) => {
|
produce<DeviceState>((draft) => {
|
||||||
draft.devices.set(id, {
|
draft.devices.set(id, {
|
||||||
|
id,
|
||||||
ready: false,
|
ready: false,
|
||||||
status: Types.DeviceStatusEnum.DEVICE_DISCONNECTED,
|
status: Types.DeviceStatusEnum.DEVICE_DISCONNECTED,
|
||||||
channels: [],
|
channels: [],
|
||||||
@@ -417,10 +419,14 @@ export const useDeviceStore = create<DeviceState>((set, get) => ({
|
|||||||
}
|
}
|
||||||
return device;
|
return device;
|
||||||
},
|
},
|
||||||
removeDevice: (id) =>
|
removeDevice: (id) => {
|
||||||
|
set(
|
||||||
produce<DeviceState>((draft) => {
|
produce<DeviceState>((draft) => {
|
||||||
draft.devices.delete(id);
|
draft.devices.delete(id);
|
||||||
}),
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
getDevices: () => Array.from(get().devices.values()),
|
getDevices: () => Array.from(get().devices.values()),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
@@ -2,9 +2,11 @@ import type React from "react";
|
|||||||
import { ChangeEvent, useState } from "react";
|
import { ChangeEvent, useState } from "react";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
AddLocationIcon,
|
||||||
IconButton,
|
IconButton,
|
||||||
majorScale,
|
majorScale,
|
||||||
Pane,
|
Pane,
|
||||||
|
Popover,
|
||||||
SendMessageIcon,
|
SendMessageIcon,
|
||||||
TextInputField,
|
TextInputField,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
@@ -14,6 +16,7 @@ import type { Channel } from "@core/stores/deviceStore.js";
|
|||||||
import { useDevice } from "@core/stores/deviceStore.js";
|
import { useDevice } from "@core/stores/deviceStore.js";
|
||||||
|
|
||||||
import { Message } from "./Message.js";
|
import { Message } from "./Message.js";
|
||||||
|
import { NewLocationMessage } from "./NewLocationMessage.js";
|
||||||
|
|
||||||
export interface ChannelChatProps {
|
export interface ChannelChatProps {
|
||||||
channel: Channel;
|
channel: Channel;
|
||||||
@@ -60,13 +63,15 @@ export const ChannelChat = ({ channel }: ChannelChatProps): JSX.Element => {
|
|||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</Pane>
|
</Pane>
|
||||||
|
<Pane display="flex" gap={majorScale(1)}>
|
||||||
<form
|
<form
|
||||||
|
style={{ display: "flex", flexGrow: 1 }}
|
||||||
onSubmit={(e): void => {
|
onSubmit={(e): void => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
sendMessage();
|
sendMessage();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Pane display="flex" gap={majorScale(1)}>
|
<Pane display="flex" flexGrow={1} gap={majorScale(1)}>
|
||||||
<TextInputField
|
<TextInputField
|
||||||
marginTop="auto"
|
marginTop="auto"
|
||||||
minLength={2}
|
minLength={2}
|
||||||
@@ -88,6 +93,16 @@ export const ChannelChat = ({ channel }: ChannelChatProps): JSX.Element => {
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Pane>
|
</Pane>
|
||||||
</form>
|
</form>
|
||||||
|
<Tooltip content="Send Location">
|
||||||
|
<Popover content={<NewLocationMessage />}>
|
||||||
|
<IconButton
|
||||||
|
icon={AddLocationIcon}
|
||||||
|
marginTop={majorScale(2)}
|
||||||
|
width={majorScale(8)}
|
||||||
|
/>
|
||||||
|
</Popover>
|
||||||
|
</Tooltip>
|
||||||
|
</Pane>
|
||||||
</Pane>
|
</Pane>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
Text,
|
Text,
|
||||||
} from "evergreen-ui";
|
} from "evergreen-ui";
|
||||||
|
|
||||||
|
import { useDevice } from "@app/core/stores/deviceStore.js";
|
||||||
import { Hashicon } from "@emeraldpay/hashicon-react";
|
import { Hashicon } from "@emeraldpay/hashicon-react";
|
||||||
import type { Protobuf, Types } from "@meshtastic/meshtasticjs";
|
import type { Protobuf, Types } from "@meshtastic/meshtasticjs";
|
||||||
|
|
||||||
@@ -30,6 +31,13 @@ export const Message = ({
|
|||||||
rxTime,
|
rxTime,
|
||||||
sender,
|
sender,
|
||||||
}: MessageProps): JSX.Element => {
|
}: MessageProps): JSX.Element => {
|
||||||
|
const { setPeerInfoOpen, setActivePeer } = useDevice();
|
||||||
|
|
||||||
|
const openPeer = (): void => {
|
||||||
|
setActivePeer(messagePacket.packet.from);
|
||||||
|
setPeerInfoOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
return lastMsgSameUser ? (
|
return lastMsgSameUser ? (
|
||||||
<Pane display="flex" marginLeft={majorScale(3)}>
|
<Pane display="flex" marginLeft={majorScale(3)}>
|
||||||
{ack ? (
|
{ack ? (
|
||||||
@@ -53,10 +61,10 @@ export const Message = ({
|
|||||||
) : (
|
) : (
|
||||||
<Pane marginX={majorScale(2)} gap={majorScale(1)} marginTop={majorScale(1)}>
|
<Pane marginX={majorScale(2)} gap={majorScale(1)} marginTop={majorScale(1)}>
|
||||||
<Pane display="flex" gap={majorScale(1)}>
|
<Pane display="flex" gap={majorScale(1)}>
|
||||||
<Pane width={majorScale(3)}>
|
<Pane onClick={openPeer} cursor="pointer" width={majorScale(3)}>
|
||||||
<Hashicon value={(sender?.num ?? 0).toString()} size={32} />
|
<Hashicon value={(sender?.num ?? 0).toString()} size={32} />
|
||||||
</Pane>
|
</Pane>
|
||||||
<Strong cursor="default" size={500}>
|
<Strong onClick={openPeer} cursor="pointer" size={500}>
|
||||||
{sender?.user?.longName ?? "UNK"}
|
{sender?.user?.longName ?? "UNK"}
|
||||||
</Strong>
|
</Strong>
|
||||||
<Small>
|
<Small>
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import type React from "react";
|
||||||
|
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
majorScale,
|
||||||
|
Pane,
|
||||||
|
SelectField,
|
||||||
|
TextInputField,
|
||||||
|
} from "evergreen-ui";
|
||||||
|
|
||||||
|
import { useDevice } from "@app/core/stores/deviceStore.js";
|
||||||
|
import { renderOptions } from "@app/core/utils/selectEnumOptions.js";
|
||||||
|
import { Protobuf } from "@meshtastic/meshtasticjs";
|
||||||
|
|
||||||
|
enum LocationType {
|
||||||
|
MGRS,
|
||||||
|
LatLng,
|
||||||
|
DecimalDegrees,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const NewLocationMessage = (): JSX.Element => {
|
||||||
|
const { connection } = useDevice();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Pane width={240} margin={majorScale(2)}>
|
||||||
|
<form
|
||||||
|
onSubmit={(e): void => {
|
||||||
|
e.preventDefault();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<TextInputField label="Name" />
|
||||||
|
<TextInputField label="Description" />
|
||||||
|
<SelectField label="Type" value={LocationType.MGRS}>
|
||||||
|
{renderOptions(LocationType)}
|
||||||
|
</SelectField>
|
||||||
|
<TextInputField label="Coordinates" />
|
||||||
|
<Button
|
||||||
|
width="100%"
|
||||||
|
onClick={() => {
|
||||||
|
void connection?.sendLocation(
|
||||||
|
Protobuf.Location.create({
|
||||||
|
latitudeI: Math.floor(3.89103 * 1e7),
|
||||||
|
longitudeI: Math.floor(105.87005 * 1e7),
|
||||||
|
name: "TEST",
|
||||||
|
description: "This is a description",
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Send
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
|
</Pane>
|
||||||
|
);
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user