diff --git a/src/pages/Map.tsx b/src/pages/Map.tsx
index 12a8e884..606f5be6 100644
--- a/src/pages/Map.tsx
+++ b/src/pages/Map.tsx
@@ -1,59 +1,93 @@
import { NodeDetail } from "@app/components/PageComponents/Map/NodeDetail";
import { Avatar } from "@app/components/UI/Avatar";
-import { Subtle } from "@app/components/UI/Typography/Subtle.tsx";
-import { cn } from "@app/core/utils/cn.ts";
import { PageLayout } from "@components/PageLayout.tsx";
import { Sidebar } from "@components/Sidebar.tsx";
-import { SidebarSection } from "@components/UI/Sidebar/SidebarSection.tsx";
-import { SidebarButton } from "@components/UI/Sidebar/sidebarButton.tsx";
import { useAppStore } from "@core/stores/appStore.ts";
import { useDevice } from "@core/stores/deviceStore.ts";
import type { Protobuf } from "@meshtastic/js";
-import { numberToHexUnpadded } from "@noble/curves/abstract/utils";
import { bbox, lineString } from "@turf/turf";
-import {
- BoxSelectIcon,
- MapPinIcon,
- ZoomInIcon,
- ZoomOutIcon,
-} from "lucide-react";
+import { MapPinIcon } from "lucide-react";
import { type JSX, useCallback, useEffect, useMemo, useState } from "react";
-import { AttributionControl, Marker, Popup, useMap } from "react-map-gl";
+import {
+ AttributionControl,
+ GeolocateControl,
+ Marker,
+ NavigationControl,
+ Popup,
+ ScaleControl,
+ useMap,
+} from "react-map-gl";
import MapGl from "react-map-gl/maplibre";
+type NodePosition = {
+ latitude: number;
+ longitude: number;
+};
+
+const convertToLatLng = (position: {
+ latitudeI?: number;
+ longitudeI?: number;
+}): NodePosition => ({
+ latitude: (position.latitudeI ?? 0) / 1e7,
+ longitude: (position.longitudeI ?? 0) / 1e7,
+});
+
const MapPage = (): JSX.Element => {
const { nodes, waypoints } = useDevice();
- const { rasterSources, darkMode } = useAppStore();
+ const { darkMode } = useAppStore();
const { default: map } = useMap();
- const [zoom, setZoom] = useState(0);
const [selectedNode, setSelectedNode] =
useState
(null);
- const allNodes = useMemo(() => Array.from(nodes.values()), [nodes]);
+ // Filter out nodes without a valid position
+ const validNodes = useMemo(
+ () =>
+ Array.from(nodes.values()).filter(
+ (node): node is Protobuf.Mesh.NodeInfo =>
+ Boolean(node.position?.latitudeI),
+ ),
+ [nodes],
+ );
+
+ const handleMarkerClick = useCallback(
+ (node: Protobuf.Mesh.NodeInfo, event: { originalEvent: MouseEvent }) => {
+ event?.originalEvent?.stopPropagation();
+
+ setSelectedNode(node);
+
+ if (map) {
+ const position = convertToLatLng(node.position);
+ map.easeTo({
+ center: [position.longitude, position.latitude],
+ zoom: map?.getZoom(),
+ });
+ }
+ },
+ [map],
+ );
- const getBBox = useCallback(() => {
+ // Get the bounds of the map based on the nodes furtherest away from center
+ const getMapBounds = useCallback(() => {
if (!map) {
return;
}
- const nodesWithPosition = allNodes.filter(
- (node) => node.position?.latitudeI,
- );
- if (!nodesWithPosition.length) {
+
+ if (!validNodes.length) {
return;
}
- if (nodesWithPosition.length === 1) {
+ if (validNodes.length === 1) {
map.easeTo({
- zoom: 12,
+ zoom: map.getZoom(),
center: [
- (nodesWithPosition[0].position?.longitudeI ?? 0) / 1e7,
- (nodesWithPosition[0].position?.latitudeI ?? 0) / 1e7,
+ (validNodes[0].position?.longitudeI ?? 0) / 1e7,
+ (validNodes[0].position?.latitudeI ?? 0) / 1e7,
],
});
return;
}
const line = lineString(
- nodesWithPosition.map((n) => [
+ validNodes.map((n) => [
(n.position?.latitudeI ?? 0) / 1e7,
(n.position?.longitudeI ?? 0) / 1e7,
]),
@@ -69,78 +103,54 @@ const MapPage = (): JSX.Element => {
if (center) {
map.easeTo(center);
}
- }, [allNodes, map]);
+ }, [validNodes, map]);
- useEffect(() => {
- map?.on("zoom", () => {
- setZoom(map?.getZoom() ?? 0);
- });
- }, [map]);
+ // Generate all markers
+ const markers = useMemo(
+ () =>
+ validNodes.map((node) => {
+ const position = convertToLatLng(node.position);
+ return (
+ handleMarkerClick(node, e)}
+ >
+
+
+ );
+ }),
+ [validNodes, handleMarkerClick],
+ );
useEffect(() => {
map?.on("load", () => {
- getBBox();
+ getMapBounds();
});
- }, [map, getBBox]);
+ }, [map, getMapBounds]);
return (
<>
-
-
- {rasterSources.map((source) => (
-
- ))}
-
-
-
+
+
{
- // const waypoint = new Protobuf.Waypoint({
- // name: "test",
- // description: "test description",
- // latitudeI: Math.trunc(e.lngLat.lat * 1e7),
- // longitudeI: Math.trunc(e.lngLat.lng * 1e7)
- // });
- // addWaypoint(waypoint);
- // connection?.sendWaypoint(waypoint, "broadcast");
- // }}
-
- // @ts-ignore
-
attributionControl={false}
renderWorldCopies={false}
maxPitch={0}
+ antialias={true}
style={{
- filter: darkMode ? "brightness(0.8)" : "",
+ filter: darkMode ? "brightness(0.9)" : "",
}}
dragRotate={false}
touchZoomRotate={false}
initialViewState={{
- zoom: 1.6,
+ zoom: 1.8,
latitude: 35,
longitude: 0,
}}
@@ -151,6 +161,14 @@ const MapPage = (): JSX.Element => {
color: darkMode ? "black" : "",
}}
/>
+
+
+
+
{waypoints.map((wp) => (
{
))}
- {/* {rasterSources.map((source, index) => (
-
- ))} */}
- {allNodes.map((node) => {
- if (node.position?.latitudeI && node.num !== selectedNode?.num) {
- return (
-
- );
- }
- })}
- {selectedNode?.position && (
+ {markers}
+ {selectedNode ? (