diff --git a/package.json b/package.json index f3189c6b..86dc9d65 100644 --- a/package.json +++ b/package.json @@ -26,13 +26,14 @@ "react-dom": "^17.0.2", "react-error-boundary": "^3.1.4", "react-file-icon": "^1.1.0", - "react-hook-form": "^7.22.5", + "react-hook-form": "^7.23.0", "react-i18next": "^11.15.3", "react-icons": "^4.3.1", "react-json-pretty": "^2.2.0", "react-qr-code": "^2.0.3", "react-redux": "^7.2.6", - "react-select": "^5.2.1", + "react-select": "^5.2.2", + "react-use-clipboard": "^1.0.7", "rfc4648": "^1.5.1", "swr": "^1.1.2", "timeago-react": "^3.0.4", @@ -66,9 +67,9 @@ "tailwindcss": "^3.0.13", "tar": "^6.1.11", "typescript": "^4.5.4", - "vite": "^2.7.10", + "vite": "^2.7.12", "vite-plugin-cdn-import": "^0.3.5", - "vite-plugin-pwa": "^0.11.12", + "vite-plugin-pwa": "^0.11.13", "workbox-window": "^6.4.2" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3f77b6a9..c32733e7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -38,13 +38,14 @@ specifiers: react-dom: ^17.0.2 react-error-boundary: ^3.1.4 react-file-icon: ^1.1.0 - react-hook-form: ^7.22.5 + react-hook-form: ^7.23.0 react-i18next: ^11.15.3 react-icons: ^4.3.1 react-json-pretty: ^2.2.0 react-qr-code: ^2.0.3 react-redux: ^7.2.6 - react-select: ^5.2.1 + react-select: ^5.2.2 + react-use-clipboard: ^1.0.7 rfc4648: ^1.5.1 swr: ^1.1.2 tailwindcss: ^3.0.13 @@ -53,9 +54,9 @@ specifiers: type-route: ^0.6.0 typescript: ^4.5.4 use-breakpoint: ^3.0.1 - vite: ^2.7.10 + vite: ^2.7.12 vite-plugin-cdn-import: ^0.3.5 - vite-plugin-pwa: ^0.11.12 + vite-plugin-pwa: ^0.11.13 workbox-window: ^6.4.2 dependencies: @@ -73,13 +74,14 @@ dependencies: react-dom: 17.0.2_react@17.0.2 react-error-boundary: 3.1.4_react@17.0.2 react-file-icon: 1.1.0_react-dom@17.0.2+react@17.0.2 - react-hook-form: 7.22.5_react@17.0.2 + react-hook-form: 7.23.0_react@17.0.2 react-i18next: 11.15.3_bc514be083f1f06b28df24d5713fc600 react-icons: 4.3.1_react@17.0.2 react-json-pretty: 2.2.0_react-dom@17.0.2+react@17.0.2 react-qr-code: 2.0.3_react@17.0.2 react-redux: 7.2.6_react-dom@17.0.2+react@17.0.2 - react-select: 5.2.1_b3482aaf5744fc7c2aeb7941b0e0a78f + react-select: 5.2.2_b3482aaf5744fc7c2aeb7941b0e0a78f + react-use-clipboard: 1.0.7_react-dom@17.0.2+react@17.0.2 rfc4648: 1.5.1 swr: 1.1.2_react@17.0.2 timeago-react: 3.0.4_react@17.0.2 @@ -113,9 +115,9 @@ devDependencies: tailwindcss: 3.0.13_ef48b3b8837f8a23677bffe8f9cd866d tar: 6.1.11 typescript: 4.5.4 - vite: 2.7.10 + vite: 2.7.12 vite-plugin-cdn-import: 0.3.5 - vite-plugin-pwa: 0.11.12_vite@2.7.10 + vite-plugin-pwa: 0.11.13_vite@2.7.12 workbox-window: 6.4.2 packages: @@ -1529,7 +1531,7 @@ packages: react: 17.0.2 react-dom: 17.0.2_react@17.0.2 react-icons: 4.3.1_react@17.0.2 - react-select: 5.2.1_b3482aaf5744fc7c2aeb7941b0e0a78f + react-select: 5.2.2_b3482aaf5744fc7c2aeb7941b0e0a78f transitivePeerDependencies: - '@babel/core' - '@types/react' @@ -2201,7 +2203,7 @@ packages: postcss: ^8.1.0 dependencies: browserslist: 4.19.1 - caniuse-lite: 1.0.30001298 + caniuse-lite: 1.0.30001299 fraction.js: 4.1.2 normalize-range: 0.1.2 picocolors: 1.0.0 @@ -2310,8 +2312,8 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001298 - electron-to-chromium: 1.4.42 + caniuse-lite: 1.0.30001299 + electron-to-chromium: 1.4.45 escalade: 3.1.1 node-releases: 2.0.1 picocolors: 1.0.0 @@ -2350,8 +2352,8 @@ packages: engines: {node: '>= 6'} dev: true - /caniuse-lite/1.0.30001298: - resolution: {integrity: sha512-AcKqikjMLlvghZL/vfTHorlQsLDhGRalYf1+GmWCf5SCMziSGjRYQW/JEksj14NaYHIR6KIhrFAy0HV5C25UzQ==} + /caniuse-lite/1.0.30001299: + resolution: {integrity: sha512-iujN4+x7QzqA2NCSrS5VUy+4gLmRd4xv6vbBBsmfVqTx8bLAD8097euLqQgKxSVLvxjSDcvF1T/i9ocgnUFexw==} dev: true /chalk/2.4.2: @@ -2458,6 +2460,12 @@ packages: safe-buffer: 5.1.2 dev: true + /copy-to-clipboard/3.3.1: + resolution: {integrity: sha512-i13qo6kIHTTpCm8/Wup+0b1mVWETvu2kIMzKoK8FpkLkFxlt0znUAHcMzox+T8sPlqtZXq3CulEjQHsYiGFJUw==} + dependencies: + toggle-selection: 1.0.6 + dev: false + /core-js-compat/3.20.2: resolution: {integrity: sha512-qZEzVQ+5Qh6cROaTPFLNS4lkvQ6mBzE3R6A6EEpssj7Zr2egMHgsy4XapdifqJDGC9CBiNv7s+ejI96rLNQFdg==} dependencies: @@ -2551,7 +2559,7 @@ packages: object-is: 1.1.5 object-keys: 1.1.1 object.assign: 4.1.2 - regexp.prototype.flags: 1.3.1 + regexp.prototype.flags: 1.3.2 side-channel: 1.0.4 which-boxed-primitive: 1.0.2 which-collection: 1.0.1 @@ -2670,8 +2678,8 @@ packages: jake: 10.8.2 dev: true - /electron-to-chromium/1.4.42: - resolution: {integrity: sha512-JJLT8bjdswJzk8sNRnQjee0MGtO4zTn1t7eWwYPr8gPTadQgNRR/wFRKLGD6HZVZby39yHERkvuCVKNm10r7Dg==} + /electron-to-chromium/1.4.45: + resolution: {integrity: sha512-czF9eYVuOmlY/vxyMQz2rGlNSjZpxNQYBe1gmQv7al171qOIhgyO9k7D5AKlgeTCSPKk+LHhj5ZyIdmEub9oNg==} dev: true /emoji-regex/8.0.0: @@ -4220,8 +4228,8 @@ packages: resolution: {integrity: sha1-sGJ44h/Gw3+lMTcysEEry2rhX1E=} dev: false - /nanoid/3.1.31: - resolution: {integrity: sha512-ZivnJm0o9bb13p2Ot5CpgC2rQdzB9Uxm/mFZweqm5eMViqOJe3PV6LU2E30SiLgheesmcPrjquqraoolONSA0A==} + /nanoid/3.1.32: + resolution: {integrity: sha512-F8mf7R3iT9bvThBoW4tGXhXFHCctyCiUUPrWF8WaTqa3h96d9QybkSeba43XVOOE3oiLfkVDe4bT8MeGmkrTxw==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true dev: true @@ -4541,7 +4549,7 @@ packages: resolution: {integrity: sha512-jBDboWM8qpaqwkMwItqTQTiFikhs/67OYVvblFFTM7MrZjt6yMKd6r2kgXizEbTTljacm4NldIlZnhbjr84QYg==} engines: {node: ^10 || ^12 || >=14} dependencies: - nanoid: 3.1.31 + nanoid: 3.1.32 picocolors: 1.0.0 source-map-js: 1.0.1 dev: true @@ -4699,8 +4707,8 @@ packages: tinycolor2: 1.4.2 dev: false - /react-hook-form/7.22.5_react@17.0.2: - resolution: {integrity: sha512-Q2zaeQFXdVQ8l3hcywhltH+Nzj4vo50wMVujHDVN/1Xy9IOaSZJwYBXA2CYTpK6rq41fnXviw3jTLb04c7Gu9Q==} + /react-hook-form/7.23.0_react@17.0.2: + resolution: {integrity: sha512-bO1JCkPAjmpuKhfUpFhsjWn2RIPgWUpep8qpMAKCoc8NM8ytBA5nDx5p99wNhZWrblYQFvU+dVy9g1oYo/JKoQ==} engines: {node: '>=12.22.0'} peerDependencies: react: ^16.8.0 || ^17 @@ -4793,8 +4801,8 @@ packages: engines: {node: '>=0.10.0'} dev: true - /react-select/5.2.1_b3482aaf5744fc7c2aeb7941b0e0a78f: - resolution: {integrity: sha512-OOyNzfKrhOcw/BlembyGWgdlJ2ObZRaqmQppPFut1RptJO423j+Y+JIsmxkvsZ4D/3CpOmwIlCvWbbAWEdh12A==} + /react-select/5.2.2_b3482aaf5744fc7c2aeb7941b0e0a78f: + resolution: {integrity: sha512-miGS2rT1XbFNjduMZT+V73xbJEeMzVkJOz727F6MeAr2hKE0uUSA8Ff7vD44H32x2PD3SRB6OXTY/L+fTV3z9w==} peerDependencies: react: ^16.8.0 || ^17.0.0 react-dom: ^16.8.0 || ^17.0.0 @@ -4827,6 +4835,17 @@ packages: react-dom: 17.0.2_react@17.0.2 dev: false + /react-use-clipboard/1.0.7_react-dom@17.0.2+react@17.0.2: + resolution: {integrity: sha512-blIprqARyITp0uVw/2Rh87mcujqXdH6vZ5NrcuXEhI5EmjBGxcGnwt/79+vdN7rwM6OliGj481lOj6ZCcsiYEQ==} + peerDependencies: + react: ^16.8.0 || ^17 + react-dom: ^16.8.0 || ^17 + dependencies: + copy-to-clipboard: 3.3.1 + react: 17.0.2 + react-dom: 17.0.2_react@17.0.2 + dev: false + /react/17.0.2: resolution: {integrity: sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==} engines: {node: '>=0.10.0'} @@ -4897,8 +4916,8 @@ packages: '@babel/runtime': 7.16.7 dev: true - /regexp.prototype.flags/1.3.1: - resolution: {integrity: sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA==} + /regexp.prototype.flags/1.3.2: + resolution: {integrity: sha512-uaro52GSI5be7+ssxjxxnLlleDBN3VHIWQHvBhfeeSXRQkuV/0Jo/hBU+omYH6NUkM+LYpTHnRRf2W/v+x7LzQ==} engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.2 @@ -5212,7 +5231,7 @@ packages: get-intrinsic: 1.1.1 has-symbols: 1.0.2 internal-slot: 1.0.3 - regexp.prototype.flags: 1.3.1 + regexp.prototype.flags: 1.3.2 side-channel: 1.0.4 dev: true @@ -5492,6 +5511,10 @@ packages: is-number: 7.0.0 dev: true + /toggle-selection/1.0.6: + resolution: {integrity: sha1-bkWxJj8gF/oKzH2J14sVuL932jI=} + dev: false + /tr46/1.0.1: resolution: {integrity: sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=} dependencies: @@ -5661,8 +5684,8 @@ packages: - rollup dev: true - /vite-plugin-pwa/0.11.12_vite@2.7.10: - resolution: {integrity: sha512-XqFmA4y9C4RBb5osSsa26GVwOSwbzf2GNVcT5+06KYYdguqLpuI9FW7iV/akZqg0OUNUpH4tHfme8SnHA4PIXA==} + /vite-plugin-pwa/0.11.13_vite@2.7.12: + resolution: {integrity: sha512-Ssj14m3TRVLfkFEAWSMcFE2d1cSdEZyrVTzfY2lSL+umHYvcIFHVDAY143sygtBCb44OPczsAOmWwBTxwOvh7g==} peerDependencies: vite: ^2.0.0 dependencies: @@ -5670,7 +5693,7 @@ packages: fast-glob: 3.2.10 pretty-bytes: 5.6.0 rollup: 2.63.0 - vite: 2.7.10 + vite: 2.7.12 workbox-build: 6.4.2 workbox-window: 6.4.2 transitivePeerDependencies: @@ -5679,8 +5702,8 @@ packages: - supports-color dev: true - /vite/2.7.10: - resolution: {integrity: sha512-KEY96ntXUid1/xJihJbgmLZx7QSC2D4Tui0FdS0Old5OokYzFclcofhtxtjDdGOk/fFpPbHv9yw88+rB93Tb8w==} + /vite/2.7.12: + resolution: {integrity: sha512-KvPYToRQWhRfBeVkyhkZ5hASuHQkqZUUdUcE3xyYtq5oYEPIJ0h9LWiWTO6v990glmSac2cEPeYeXzpX5Z6qKQ==} engines: {node: '>=12.2.0'} hasBin: true peerDependencies: diff --git a/src/components/Channel.tsx b/src/components/Channel.tsx index 1dd3aceb..9787262c 100644 --- a/src/components/Channel.tsx +++ b/src/components/Channel.tsx @@ -181,7 +181,7 @@ export const Channel = ({ channel }: ChannelProps): JSX.Element => { - + {loading && }
diff --git a/src/components/Map/Marker.tsx b/src/components/Map/Marker.tsx index b7b16701..9fd267d5 100644 --- a/src/components/Map/Marker.tsx +++ b/src/components/Map/Marker.tsx @@ -2,20 +2,17 @@ import React from 'react'; import ReactDOM from 'react-dom'; import mapbox from 'mapbox-gl'; -import ReactDOMServer from 'react-dom/server'; import { useMapbox } from '@hooks/useMapbox'; export interface MarkerProps extends Omit { children?: React.ReactNode; center: mapbox.LngLatLike; - popup: JSX.Element; } export const Marker = ({ children, center, - popup, ...props }: MarkerProps): JSX.Element => { const { map } = useMapbox(); @@ -23,14 +20,10 @@ export const Marker = ({ const addMarker = React.useCallback((): void => { if (map) { - const marker = new mapbox.Marker(ref.current, props) - .setLngLat(center) - .setPopup( - new mapbox.Popup().setHTML(ReactDOMServer.renderToString(popup)), - ); + const marker = new mapbox.Marker(ref.current, props).setLngLat(center); marker.addTo(map); } - }, [map, center, props, popup]); + }, [map, center, props]); React.useEffect(() => { map?.on('load', () => { diff --git a/src/components/TabButton.tsx b/src/components/TabButton.tsx new file mode 100644 index 00000000..8b60be50 --- /dev/null +++ b/src/components/TabButton.tsx @@ -0,0 +1,27 @@ +import type React from 'react'; + +import { Tab } from '@headlessui/react'; + +export interface TabButtonProps { + children: React.ReactNode; +} + +export const TabButton = ({ children }: TabButtonProps): JSX.Element => { + return ( + + `border-gray-300 hover:border-b-2 dark:border-gray-600 w-full ${ + selected ? 'border-b-2' : 'border-b-0' + } ` + } + > +
+
+
+ {children} +
+
+
+
+ ); +}; diff --git a/src/index.css b/src/index.css index 9b3ee91e..b855a35f 100644 --- a/src/index.css +++ b/src/index.css @@ -33,3 +33,26 @@ box-sizing: unset !important; padding: 0 !important; } + +.__json-pretty__ { + line-height: 1.3; + color: #748096; + overflow: auto; +} +.__json-pretty__ .__json-key__ { + color: #b553bf; +} +.__json-pretty__ .__json-value__ { + color: #93a3bf; +} +.__json-pretty__ .__json-string__ { + color: #fba856; +} +.__json-pretty__ .__json-boolean__ { + color: #448aa9; +} +.__json-pretty-error__ { + line-height: 1.3; + color: #748096; + overflow: auto; +} diff --git a/src/index.tsx b/src/index.tsx index 8673da63..d0708893 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,6 +1,5 @@ import '@meshtastic/components/dist/style.css'; import '@app/index.css'; -import 'react-json-pretty/themes/monikai.css'; import '@core/translation'; import React from 'react'; diff --git a/src/pages/Nodes/Index.tsx b/src/pages/Nodes/Index.tsx index c343ccb8..253a1458 100644 --- a/src/pages/Nodes/Index.tsx +++ b/src/pages/Nodes/Index.tsx @@ -1,7 +1,10 @@ import React from 'react'; -import { FiXCircle } from 'react-icons/fi'; +import mapbox from 'mapbox-gl'; +import { FiMapPin, FiXCircle } from 'react-icons/fi'; +import { Marker } from '@app/components/Map/Marker'; +import type { Node } from '@app/core/slices/meshtasticSlice.js'; import { Drawer } from '@components/generic/Drawer'; import { Map } from '@components/Map'; import { useAppSelector } from '@hooks/useAppSelector'; @@ -9,6 +12,7 @@ import { useBreakpoint } from '@hooks/useBreakpoint'; import { IconButton } from '@meshtastic/components'; import { NodeCard } from './NodeCard'; +import { Sidebar } from './Sidebar'; export const Nodes = (): JSX.Element => { const myNodeInfo = useAppSelector((state) => state.meshtastic.radio.hardware); @@ -25,6 +29,8 @@ export const Nodes = (): JSX.Element => { const [navOpen, setNavOpen] = React.useState(false); const { breakpoint } = useBreakpoint(); + const [sidebarOpen, setSidebarOpen] = React.useState(false); + const [selectedNode, setSelectedNode] = React.useState(); return (
@@ -36,7 +42,7 @@ export const Nodes = (): JSX.Element => { }} >
-
+
Nodes
@@ -53,15 +59,67 @@ export const Nodes = (): JSX.Element => { No nodes found. )} - {myNode && } + {myNode && ( + { + setSelectedNode(myNode); + setSidebarOpen(true); + }} + /> + )} {nodes .filter((node) => node.number !== myNodeInfo.myNodeNum) .map((node) => ( - + { + setSelectedNode(node); + setSidebarOpen(true); + }} + /> ))} + {nodes.map((node) => { + return ( + node.currentPosition && ( + +
{ + setSelectedNode(node); + setSidebarOpen(true); + }} + className="z-50 bg-blue-500 border-2 border-blue-500 rounded-full bg-opacity-30" + > +
+ +
+
+
+ ) + ); + })} + + + {sidebarOpen && selectedNode && ( + { + setSidebarOpen(false); + }} + node={selectedNode} + /> + )}
); }; diff --git a/src/pages/Nodes/NodeCard.tsx b/src/pages/Nodes/NodeCard.tsx index 61be705c..43b2dec6 100644 --- a/src/pages/Nodes/NodeCard.tsx +++ b/src/pages/Nodes/NodeCard.tsx @@ -1,51 +1,42 @@ import React from 'react'; import mapbox from 'mapbox-gl'; -import { FaSatellite } from 'react-icons/fa'; -import { FiCode, FiMapPin } from 'react-icons/fi'; -import { GiLightningFrequency } from 'react-icons/gi'; +import { FiAlignLeft } from 'react-icons/fi'; import { MdAccountCircle, - MdArrowDropDown, - MdArrowDropUp, MdGpsFixed, MdGpsNotFixed, MdGpsOff, - MdSdStorage, - MdSignalCellularAlt, } from 'react-icons/md'; -import JSONPretty from 'react-json-pretty'; import TimeAgo from 'timeago-react'; -import { Modal } from '@components/generic/Modal'; -import { Marker } from '@components/Map/Marker'; import type { Node } from '@core/slices/meshtasticSlice'; -import { Disclosure } from '@headlessui/react'; import { useMapbox } from '@hooks/useMapbox'; -import { Card, IconButton } from '@meshtastic/components'; -import { Protobuf } from '@meshtastic/meshtasticjs'; +import { IconButton } from '@meshtastic/components'; type PositionConfidence = 'high' | 'low' | 'none'; type NodeAge = 'young' | 'aging' | 'old' | 'dead'; export interface NodeCardProps { node: Node; - myNodeInfo?: Protobuf.MyNodeInfo; + isMyNode?: boolean; + setSelected: () => void; } -export const NodeCard = ({ node, myNodeInfo }: NodeCardProps): JSX.Element => { - const [snrAverage, setSnrAverage] = React.useState(0); - const [satsAverage, setSatsAverage] = React.useState(0); - const [showDebug, setShowDebug] = React.useState(false); +export const NodeCard = ({ + node, + isMyNode, + setSelected, +}: NodeCardProps): JSX.Element => { const { map } = useMapbox(); - React.useEffect(() => { - setSnrAverage( - node.snr - .slice(node.snr.length - 3, node.snr.length) - .reduce((a, b) => a + b) / (node.snr.length > 3 ? 3 : node.snr.length), - ); - }, [node.snr]); + // React.useEffect(() => { + // setSnrAverage( + // node.snr + // .slice(node.snr.length - 3, node.snr.length) + // .reduce((a, b) => a + b) / (node.snr.length > 3 ? 3 : node.snr.length), + // ); + // }, [node.snr]); const [PositionConfidence, setPositionConfidence] = React.useState('none'); const [age, setAge] = React.useState('young'); @@ -72,165 +63,69 @@ export const NodeCard = ({ node, myNodeInfo }: NodeCardProps): JSX.Element => { : 'none', ); }, [node.currentPosition]); - - // React.useEffect(() => { - // setSatsAverage( - // node.positions - // .filter((pos) => pos.satsInView !== 0) - // .slice(node.positions.length - 3, node.positions.length) - // .reduce((a, b) => { - // return a.satsInView + b.satsInView; - // }).satsInView / (node.positions.length > 3 ? 3 : node.positions.length), - // ); - // }, [node.positions]); - return ( - <> - { - setShowDebug(false); - }} - > - -
- -
-
-
- {node.currentPosition && ( - +
+ {isMyNode ? ( + + ) : ( +
+ )} +
{node.user?.longName}
+ +
+ {!isMyNode && ( + + {node.lastHeard.getTime() ? ( + + ) : ( + 'Never' + )} + + )} +
+ { + e.stopPropagation(); + if (PositionConfidence !== 'none' && node.currentPosition) { + map?.flyTo({ + center: new mapbox.LngLat( + node.currentPosition.longitudeI / 1e7, + node.currentPosition.latitudeI / 1e7, + ), + zoom: 16, + }); + } + }} + icon={ + PositionConfidence === 'high' ? ( + + ) : PositionConfidence === 'low' ? ( + + ) : ( + ) } - popup={
Popup
} - > -
-
- -
-
- - )} - - - - {myNodeInfo ? ( - - ) : ( -
- )} -
{node.user?.longName}
- -
- {!myNodeInfo && ( - - {node.lastHeard.getTime() ? ( - - ) : ( - 'Never' - )} - - )} -
- { - e.stopPropagation(); - if (PositionConfidence !== 'none' && node.currentPosition) { - map?.flyTo({ - center: new mapbox.LngLat( - node.currentPosition.longitudeI / 1e7, - node.currentPosition.latitudeI / 1e7, - ), - zoom: 16, - }); - } - }} - icon={ - PositionConfidence === 'high' ? ( - - ) : PositionConfidence === 'low' ? ( - - ) : ( - - ) - } - /> - - - {myNodeInfo && ( -
-
- - - Firmware Ver: - - {myNodeInfo.firmwareVersion} -
-
- - - Freq Bands: - - {myNodeInfo.numBands} -
-
- )} -
-
- {Protobuf.HardwareModel[node.user?.hwModel ?? 0]} -
-
- { - setShowDebug(true); - }} - icon={} - /> -
-
-
- - SNR: - {node.snr[node.snr.length - 1] < snrAverage ? ( - - ) : ( - - )} - {node.snr[node.snr.length - 1]}, Average: {snrAverage} -
-
- - Sats: - {(node.currentPosition?.satsInView ?? 0) < satsAverage ? ( - - ) : ( - - )} - {node.currentPosition?.satsInView ?? 0}, Average: {satsAverage} -
-
- - + /> + { + setSelected(); + }} + icon={} + /> + {/* */} +
+
); }; diff --git a/src/pages/Nodes/Sidebar.tsx b/src/pages/Nodes/Sidebar.tsx new file mode 100644 index 00000000..c56ea0a2 --- /dev/null +++ b/src/pages/Nodes/Sidebar.tsx @@ -0,0 +1,98 @@ +import React from 'react'; + +import { + FiCheck, + FiClipboard, + FiCode, + FiMapPin, + FiSliders, + FiUser, + FiX, +} from 'react-icons/fi'; +import { IoTelescope } from 'react-icons/io5'; +import JSONPretty from 'react-json-pretty'; +import useCopyClipboard from 'react-use-clipboard'; + +import { TabButton } from '@app/components/TabButton'; +import type { Node } from '@app/core/slices/meshtasticSlice'; +import { Tab } from '@headlessui/react'; +import { IconButton } from '@meshtastic/components'; + +export interface SidebarProps { + node: Node; + closeSidebar: () => void; +} + +export const Sidebar = ({ node, closeSidebar }: SidebarProps): JSX.Element => { + const [toCopy, setToCopy] = React.useState(''); + const [isCopied, setCopied] = useCopyClipboard(toCopy, { + successDuration: 1000, + }); + + return ( +
+ +
+
+
+
+

+ {node.number} +

+

+ {node.user?.longName}({node.user?.shortName}) +

+
+
+ { + closeSidebar(); + }} + icon={} + /> +
+
+
+ + + + + + + + + + + + + + + + + + +
+ + Content 1 + +
+
+ Content 3 + Remote Administration + +
+ { + setToCopy(JSON.stringify(node)); + setCopied(); + }} + icon={isCopied ? : } + /> +
+ +
+
+
+
+ ); +};