diff --git a/package.json b/package.json index 4099d308..6c201e3c 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "evergreen-ui": "^6.10.3", "geodesy": "^2.4.0", "immer": "^9.0.15", + "mapbox-gl": "npm:empty-npm-package@1.0.0", "maplibre-gl": "^2.3.0", "modern-css-reset": "^1.4.0", "prettier": "^2.7.1", @@ -38,6 +39,7 @@ "react-hook-form": "^7.34.2", "react-icons": "^4.4.0", "react-json-pretty": "^2.2.0", + "react-map-gl": "^7.0.19", "react-qrcode-logo": "^2.8.0", "rfc4648": "^1.5.2", "zustand": "4.1.0" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 29ef71d7..725c8507 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,6 +20,7 @@ specifiers: geodesy: ^2.4.0 gzipper: ^7.1.0 immer: ^9.0.15 + mapbox-gl: npm:empty-npm-package@1.0.0 maplibre-gl: ^2.3.0 modern-css-reset: ^1.4.0 prettier: ^2.7.1 @@ -28,6 +29,7 @@ specifiers: react-hook-form: ^7.34.2 react-icons: ^4.4.0 react-json-pretty: ^2.2.0 + react-map-gl: ^7.0.19 react-qrcode-logo: ^2.8.0 rfc4648: ^1.5.2 rollup-plugin-visualizer: ^5.7.1 @@ -49,6 +51,7 @@ dependencies: evergreen-ui: 6.10.3_biqbaboplfbrettd7655fr4n2y geodesy: 2.4.0 immer: 9.0.15 + mapbox-gl: /empty-npm-package/1.0.0 maplibre-gl: 2.3.0 modern-css-reset: 1.4.0 prettier: 2.7.1 @@ -57,6 +60,7 @@ dependencies: react-hook-form: 7.34.2_react@18.2.0 react-icons: 4.4.0_react@18.2.0 react-json-pretty: 2.2.0_biqbaboplfbrettd7655fr4n2y + react-map-gl: 7.0.19_6eczaga5xxiwzxtfiyk6fioasq react-qrcode-logo: 2.8.0_biqbaboplfbrettd7655fr4n2y rfc4648: 1.5.2 zustand: 4.1.0_immer@9.0.15+react@18.2.0 @@ -739,6 +743,12 @@ packages: resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} dev: false + /@types/mapbox-gl/2.7.5: + resolution: {integrity: sha512-T8gACm3oGKMlBo2l/9vnKEAxgCc0g2mr8g6dI1d3ZO6EzRe7JALBONlWRmc7SOHV79kiarkcdLdDVEnfd+jilA==} + dependencies: + '@types/geojson': 7946.0.10 + dev: false + /@types/mapbox__point-geometry/0.1.2: resolution: {integrity: sha512-D0lgCq+3VWV85ey1MZVkE8ZveyuvW5VAfuahVTQRpXFQTxw03SuIf1/K4UQ87MMIXVKzpFjXFiFMZzLj2kU+iA==} dev: false @@ -1342,6 +1352,10 @@ packages: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} dev: true + /empty-npm-package/1.0.0: + resolution: {integrity: sha512-q4Mq/+XO7UNDdMiPpR/LIBIW1Zl4V0Z6UT9aKGqIAnBCtCb3lvZJM1KbDbdzdC8fKflwflModfjR29Nt0EpcwA==} + dev: false + /encoding/0.1.13: resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} dependencies: @@ -2982,6 +2996,17 @@ packages: react-dom: 18.2.0_react@18.2.0 dev: false + /react-map-gl/7.0.19_6eczaga5xxiwzxtfiyk6fioasq: + resolution: {integrity: sha512-s3E8aU6BursSDVwzQZbzdDzDRUX4kdfRqZODYvkdDbInr3RzaNdlbhJ5tojXKWOonsxV6wt+Acv9JCaOMKaf0A==} + peerDependencies: + mapbox-gl: '*' + react: '>=16.3.0' + dependencies: + '@types/mapbox-gl': 2.7.5 + mapbox-gl: /empty-npm-package/1.0.0 + react: 18.2.0 + dev: false + /react-qrcode-logo/2.8.0_biqbaboplfbrettd7655fr4n2y: resolution: {integrity: sha512-dbEgjsg6C4tK6+oGmCRDlJ7urQAWqybaGBLJtp8Z6ZYvVUP302HBXk/F7VRk54yWTwzlHwig3VK6HMKCha2YFw==} peerDependencies: diff --git a/src/App.tsx b/src/App.tsx index 9c4d22ad..2dee6056 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,6 +1,7 @@ import type React from "react"; import { Pane } from "evergreen-ui"; +import { MapProvider } from "react-map-gl"; import { AppLayout } from "@components/layout/AppLayout.js"; @@ -10,7 +11,9 @@ export const App = (): JSX.Element => { return ( - + + + ); diff --git a/src/core/stores/deviceStore.ts b/src/core/stores/deviceStore.ts index 7af9a7b1..d6621e34 100644 --- a/src/core/stores/deviceStore.ts +++ b/src/core/stores/deviceStore.ts @@ -44,10 +44,10 @@ export interface Device { activePage: Page; peerInfoOpen: boolean; activePeer: number; + waypoints: Protobuf.Location[]; setReady(ready: boolean): void; setStatus: (status: Types.DeviceStatusEnum) => void; - addChannel: (channel: Channel) => void; setConfig: (config: Protobuf.Config) => void; setModuleConfig: (config: Protobuf.ModuleConfig) => void; setHardware: (hardware: Protobuf.MyNodeInfo) => void; @@ -55,6 +55,8 @@ export interface Device { setActivePage: (page: Page) => void; setPeerInfoOpen: (open: boolean) => void; setActivePeer: (peer: number) => void; + addChannel: (channel: Channel) => void; + addWaypoint: (waypoint: Protobuf.Location) => void; addNodeInfo: (nodeInfo: Types.NodeInfoPacket) => void; addUser: (user: Types.UserPacket) => void; addPosition: (position: Types.PositionPacket) => void; @@ -92,6 +94,7 @@ export const useDeviceStore = create((set, get) => ({ activePage: "messages", peerInfoOpen: false, activePeer: 0, + waypoints: [], setReady: (ready: boolean) => { set( @@ -113,25 +116,6 @@ export const useDeviceStore = create((set, get) => ({ }) ); }, - addChannel: (channel: Channel) => { - set( - produce((draft) => { - const device = draft.devices.get(id); - if (device) { - const channelIndex = device.channels.findIndex( - (c) => c.config.index === channel.config.index - ); - if (channelIndex !== -1) { - const messages = device.channels[channelIndex].messages; - device.channels[channelIndex] = channel; - device.channels[channelIndex].messages = messages; - } else { - device.channels.push(channel); - } - } - }) - ); - }, setConfig: (config: Protobuf.Config) => { set( produce((draft) => { @@ -257,6 +241,43 @@ export const useDeviceStore = create((set, get) => ({ }) ); }, + addChannel: (channel: Channel) => { + set( + produce((draft) => { + const device = draft.devices.get(id); + if (device) { + const channelIndex = device.channels.findIndex( + (c) => c.config.index === channel.config.index + ); + if (channelIndex !== -1) { + const messages = device.channels[channelIndex].messages; + device.channels[channelIndex] = channel; + device.channels[channelIndex].messages = messages; + } else { + device.channels.push(channel); + } + } + }) + ); + }, + addWaypoint: (waypoint: Protobuf.Location) => { + set( + produce((draft) => { + const device = draft.devices.get(id); + if (device) { + const waypointIndex = device.waypoints.findIndex( + (wp) => wp.id === waypoint.id + ); + + if (waypointIndex !== -1) { + device.waypoints[waypointIndex] = waypoint; + } else { + device.waypoints.push(waypoint); + } + } + }) + ); + }, addNodeInfo: (nodeInfo) => { set( produce((draft) => { diff --git a/src/core/subscriptions.ts b/src/core/subscriptions.ts index 0ee1af33..07a136bb 100644 --- a/src/core/subscriptions.ts +++ b/src/core/subscriptions.ts @@ -71,5 +71,8 @@ export const subscribeAll = (device: Device, connection: IConnection) => { ? new Date(messagePacket.packet.rxTime * 1000) : new Date(), }); + if (messagePacket.location) { + device.addWaypoint(messagePacket.location); + } }); }; diff --git a/src/pages/Map/index.tsx b/src/pages/Map/index.tsx index 6eb75249..8fbcd29c 100644 --- a/src/pages/Map/index.tsx +++ b/src/pages/Map/index.tsx @@ -1,49 +1,44 @@ import type React from "react"; -import { useEffect, useMemo, useRef } from "react"; +import { useRef } from "react"; -import { majorScale, Pane } from "evergreen-ui"; -import { Marker } from "maplibre-gl"; +import { majorScale, MapMarkerIcon, Pane } from "evergreen-ui"; +import maplibregl from "maplibre-gl"; +import Map, { Marker } from "react-map-gl"; -import { useCreateMapbox } from "@app/core/providers/useCreateMapbox.js"; import { useDevice } from "@core/providers/useDevice.js"; +import { Hashicon } from "@emeraldpay/hashicon-react"; export const MapPage = (): JSX.Element => { - const { nodes } = useDevice(); + const { nodes, waypoints } = useDevice(); - const nodeMarkers = useMemo(() => new Map(), []); + // const nodeMarkers = useMemo(() => new Map(), []); const ref = useRef(null); - const map = useCreateMapbox({ - ref, - style: - "https://raw.githubusercontent.com/hc-oss/maplibre-gl-styles/master/styles/osm-mapnik/v8/default.json", - }); - - useEffect(() => { - nodes.map((n) => { - if (n.data.position?.longitudeI && n.data.position?.latitudeI && map) { - if (nodeMarkers.has(n.data.num)) { - nodeMarkers - .get(n.data.num) - ?.setLngLat([ - n.data.position?.longitudeI / 1e7, - n.data.position?.latitudeI / 1e7, - ]); - } else { - nodeMarkers.set( - n.data.num, - new Marker() - .setLngLat([ - n.data.position?.longitudeI / 1e7, - n.data.position?.latitudeI / 1e7, - ]) - .addTo(map) - ); - } - } - }); - }, [map, nodeMarkers, nodes]); + // useEffect(() => { + // nodes.map((n) => { + // if (n.data.position?.longitudeI && n.data.position?.latitudeI && map) { + // if (nodeMarkers.has(n.data.num)) { + // nodeMarkers + // .get(n.data.num) + // ?.setLngLat([ + // n.data.position?.longitudeI / 1e7, + // n.data.position?.latitudeI / 1e7, + // ]); + // } else { + // nodeMarkers.set( + // n.data.num, + // new Marker() + // .setLngLat([ + // n.data.position?.longitudeI / 1e7, + // n.data.position?.latitudeI / 1e7, + // ]) + // .addTo(map) + // ); + // } + // } + // }); + // }, [map, nodeMarkers, nodes]); return ( { gap={majorScale(2)} overflow="hidden" > - + + {waypoints.map((wp) => ( + + + + + + ))} + {nodes + .filter((n) => n.data.position?.latitudeI) + .map((n) => { + if (n.data.position?.latitudeI) { + return ( + + + + + + ); + } + })} + ); };