mirror of
https://github.com/dpup/meshstream.git
synced 2026-08-09 10:12:52 +02:00
Docker set up and fixes for build
This commit is contained in:
@@ -27,19 +27,6 @@ const calculateZoomFromPrecisionBits = (precisionBits?: number): number => {
|
||||
return Math.min(18, baseZoom + (additionalZoom / 2)); // Cap at zoom 18
|
||||
};
|
||||
|
||||
// Function to calculate accuracy in meters from precision bits
|
||||
const calculateAccuracyFromPrecisionBits = (precisionBits?: number): number => {
|
||||
if (!precisionBits) return 300; // Default accuracy of 300m
|
||||
|
||||
// Each precision bit halves the accuracy radius
|
||||
// Starting with Earth's circumference (~40075km), calculate the precision
|
||||
const earthCircumference = 40075000; // in meters
|
||||
const accuracy = earthCircumference / (2 ** precisionBits) / 2;
|
||||
|
||||
// Limit to reasonable values
|
||||
return Math.max(1, Math.min(accuracy, 10000));
|
||||
};
|
||||
|
||||
export const Map: React.FC<MapProps> = ({
|
||||
latitude,
|
||||
longitude,
|
||||
@@ -55,11 +42,6 @@ export const Map: React.FC<MapProps> = ({
|
||||
// Calculate zoom level based on precision bits if zoom is not provided
|
||||
const effectiveZoom = zoom || calculateZoomFromPrecisionBits(precisionBits);
|
||||
|
||||
// Calculate accuracy in meters if we have precision bits
|
||||
const accuracyMeters = precisionBits !== undefined
|
||||
? calculateAccuracyFromPrecisionBits(precisionBits)
|
||||
: undefined;
|
||||
|
||||
const mapUrl = getStaticMapUrl(
|
||||
latitude,
|
||||
longitude,
|
||||
@@ -67,8 +49,7 @@ export const Map: React.FC<MapProps> = ({
|
||||
width,
|
||||
height,
|
||||
nightMode,
|
||||
precisionBits,
|
||||
accuracyMeters
|
||||
precisionBits
|
||||
);
|
||||
const googleMapsUrl = getGoogleMapsUrl(latitude, longitude);
|
||||
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
import React from "react";
|
||||
import { Packet } from "../lib/types";
|
||||
|
||||
interface MessageDisplayProps {
|
||||
message: Packet;
|
||||
}
|
||||
|
||||
export const MessageDisplay: React.FC<MessageDisplayProps> = ({ message }) => {
|
||||
const { data } = message;
|
||||
|
||||
const getMessageContent = () => {
|
||||
if (data.text_message) {
|
||||
return data.text_message;
|
||||
} else if (data.position) {
|
||||
return `Position: ${data.position.latitude}, ${data.position.longitude}`;
|
||||
} else if (data.node_info) {
|
||||
return `Node Info: ${data.node_info.longName || data.node_info.shortName}`;
|
||||
} else if (data.telemetry) {
|
||||
return "Telemetry data";
|
||||
} else if (data.decode_error) {
|
||||
return `Error: ${data.decode_error}`;
|
||||
}
|
||||
return "Unknown message type";
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="p-4 border border-neutral-700 rounded bg-neutral-800 shadow-inner">
|
||||
<div className="flex justify-between mb-2">
|
||||
<span className="font-medium text-neutral-200">
|
||||
From: {data.from || "Unknown"}
|
||||
</span>
|
||||
<span className="text-neutral-400 text-sm">
|
||||
ID: {data.id || "No ID"}
|
||||
</span>
|
||||
</div>
|
||||
<div className="mb-2 text-neutral-300">{getMessageContent()}</div>
|
||||
<div className="mt-3 flex justify-between items-center">
|
||||
<span className="text-xs text-neutral-500">
|
||||
Channel: {message.info.channel}
|
||||
</span>
|
||||
<span className="text-xs text-neutral-500">Type: {data.port_num}</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -144,7 +144,7 @@ export const NetworkMap = React.forwardRef<{ resetAutoZoom: () => void }, Networ
|
||||
}
|
||||
|
||||
// Update markers and fit the map
|
||||
updateNodeMarkers(nodesWithPosition, navigate);
|
||||
updateNodeMarkers(nodesWithPosition);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("Error initializing map:", error);
|
||||
@@ -153,7 +153,7 @@ export const NetworkMap = React.forwardRef<{ resetAutoZoom: () => void }, Networ
|
||||
}
|
||||
console.warn("Cannot initialize map - prerequisites not met");
|
||||
return false;
|
||||
}, [nodesWithPosition, navigate, updateNodeMarkers, initializeMap]);
|
||||
}, [nodesWithPosition, updateNodeMarkers, initializeMap]);
|
||||
|
||||
// Check for Google Maps API loading - make sure all required objects are available
|
||||
useEffect(() => {
|
||||
@@ -274,7 +274,7 @@ export const NetworkMap = React.forwardRef<{ resetAutoZoom: () => void }, Networ
|
||||
}
|
||||
|
||||
// Helper function to update node markers on the map
|
||||
function updateNodeMarkers(nodes: MapNode[], navigate: ReturnType<typeof useNavigate>): void {
|
||||
function updateNodeMarkers(nodes: MapNode[]): void {
|
||||
if (!mapInstanceRef.current) return;
|
||||
|
||||
// Clear the bounds for recalculation
|
||||
@@ -306,7 +306,7 @@ export const NetworkMap = React.forwardRef<{ resetAutoZoom: () => void }, Networ
|
||||
|
||||
// Create or update marker
|
||||
if (!markersRef.current[key]) {
|
||||
createMarker(node, position, nodeName, navigate);
|
||||
createMarker(node, position, nodeName);
|
||||
} else {
|
||||
updateMarker(node, position);
|
||||
}
|
||||
@@ -330,8 +330,7 @@ export const NetworkMap = React.forwardRef<{ resetAutoZoom: () => void }, Networ
|
||||
function createMarker(
|
||||
node: MapNode,
|
||||
position: google.maps.LatLngLiteral,
|
||||
nodeName: string,
|
||||
navigate: ReturnType<typeof useNavigate>
|
||||
nodeName: string
|
||||
): void {
|
||||
if (!mapInstanceRef.current || !infoWindowRef.current) return;
|
||||
|
||||
@@ -369,7 +368,7 @@ export const NetworkMap = React.forwardRef<{ resetAutoZoom: () => void }, Networ
|
||||
|
||||
// Add click listener to show info window
|
||||
marker.addListener('gmp-click', () => {
|
||||
showInfoWindow(node, marker, navigate);
|
||||
showInfoWindow(node, marker);
|
||||
});
|
||||
|
||||
markersRef.current[key] = marker;
|
||||
@@ -412,8 +411,7 @@ export const NetworkMap = React.forwardRef<{ resetAutoZoom: () => void }, Networ
|
||||
// Show info window for a node
|
||||
function showInfoWindow(
|
||||
node: MapNode,
|
||||
marker: google.maps.marker.AdvancedMarkerElement,
|
||||
navigate: ReturnType<typeof useNavigate>
|
||||
marker: google.maps.marker.AdvancedMarkerElement
|
||||
): void {
|
||||
if (!infoWindowRef.current || !mapInstanceRef.current) return;
|
||||
|
||||
@@ -445,8 +443,7 @@ export const NetworkMap = React.forwardRef<{ resetAutoZoom: () => void }, Networ
|
||||
<div style="font-size: 12px; margin-bottom: 8px; color: #333;">
|
||||
Packets: ${node.messageCount || 0} · Text: ${node.textMessageCount || 0}
|
||||
</div>
|
||||
<a href="javascript:void(0);"
|
||||
id="view-node-${node.id}"
|
||||
<a href="/node/${node.id.toString(16)}"
|
||||
style="font-size: 13px; color: #3b82f6; text-decoration: none; font-weight: 500; display: inline-block; padding: 4px 8px; background-color: #f1f5f9; border-radius: 4px;">
|
||||
View details →
|
||||
</a>
|
||||
@@ -455,16 +452,6 @@ export const NetworkMap = React.forwardRef<{ resetAutoZoom: () => void }, Networ
|
||||
|
||||
infoWindowRef.current.setContent(infoContent);
|
||||
infoWindowRef.current.open(mapInstanceRef.current, marker);
|
||||
|
||||
// Add listener for the "View details" link with a delay to allow DOM to update
|
||||
setTimeout(() => {
|
||||
const link = document.getElementById(`view-node-${node.id}`);
|
||||
if (link) {
|
||||
link.addEventListener('gmp-click', () => {
|
||||
navigate({ to: `/node/$nodeId`, params: { nodeId: node.id.toString(16) } });
|
||||
});
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
|
||||
// Prepare the styling for the map container
|
||||
|
||||
@@ -27,7 +27,6 @@ import { Separator } from "../Separator";
|
||||
import { KeyValuePair } from "../ui/KeyValuePair";
|
||||
import { Section } from "../ui/Section";
|
||||
import { BatteryLevel } from "./BatteryLevel";
|
||||
import { NetworkStrength } from "./NetworkStrength";
|
||||
import { GoogleMap } from "./GoogleMap";
|
||||
import { NodePositionData } from "./NodePositionData";
|
||||
import { EnvironmentMetrics } from "./EnvironmentMetrics";
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
export * from './PacketList';
|
||||
export * from './MessageDisplay';
|
||||
export * from './PacketDetails';
|
||||
export * from './Filter';
|
||||
export * from './InfoMessage';
|
||||
|
||||
@@ -43,7 +43,6 @@ export const DeviceMetricsPacket: React.FC<DeviceMetricsPacketProps> = ({
|
||||
icon={<Gauge />}
|
||||
iconBgColor="bg-amber-500"
|
||||
label="Device Telemetry"
|
||||
backgroundColor="bg-amber-950/5"
|
||||
>
|
||||
<div className="space-y-3">
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
|
||||
@@ -121,7 +121,6 @@ export const EnvironmentMetricsPacket: React.FC<
|
||||
icon={<Thermometer />}
|
||||
iconBgColor="bg-emerald-700"
|
||||
label="Environment Telemetry"
|
||||
backgroundColor="bg-green-950/5"
|
||||
>
|
||||
<div className="space-y-3">
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
|
||||
@@ -20,7 +20,6 @@ export const ErrorPacket: React.FC<ErrorPacketProps> = ({ packet }) => {
|
||||
icon={<AlertTriangle />}
|
||||
iconBgColor="bg-red-500"
|
||||
label="Error"
|
||||
backgroundColor="bg-red-950/5"
|
||||
>
|
||||
<div className="max-w-md">
|
||||
<div className="text-red-400 mb-2 font-medium">
|
||||
|
||||
@@ -46,7 +46,6 @@ export const GenericPacket: React.FC<GenericPacketProps> = ({ packet }) => {
|
||||
icon={<Package />}
|
||||
iconBgColor="bg-slate-500"
|
||||
label={portName.replace("_APP", "")}
|
||||
backgroundColor="bg-slate-950/5"
|
||||
>
|
||||
<div className="max-w-md">
|
||||
<KeyValueGrid>
|
||||
|
||||
@@ -21,7 +21,6 @@ export const NodeInfoPacket: React.FC<NodeInfoPacketProps> = ({ packet }) => {
|
||||
icon={<User />}
|
||||
iconBgColor="bg-purple-500"
|
||||
label="Node Info"
|
||||
backgroundColor="bg-purple-950/5"
|
||||
>
|
||||
<div className="flex flex-col gap-1.5">
|
||||
{/* First row: Long name and short name */}
|
||||
|
||||
@@ -35,7 +35,7 @@ export const PacketCard: React.FC<PacketCardProps> = ({
|
||||
>
|
||||
{React.cloneElement(icon as React.ReactElement, {
|
||||
className: "h-3.5 w-3.5 text-white",
|
||||
})}
|
||||
} as React.HTMLAttributes<HTMLElement>)}
|
||||
</div>
|
||||
{data.from ? (
|
||||
<Link
|
||||
|
||||
@@ -57,7 +57,6 @@ export const TelemetryPacket: React.FC<TelemetryPacketProps> = ({ packet }) => {
|
||||
icon={<BarChart />}
|
||||
iconBgColor="bg-neutral-500"
|
||||
label="Unknown Telemetry"
|
||||
backgroundColor="bg-neutral-950/5"
|
||||
>
|
||||
<div className="text-neutral-400 text-sm">
|
||||
Unknown telemetry data received at{' '}
|
||||
|
||||
@@ -16,7 +16,6 @@ export const TextMessagePacket: React.FC<TextMessagePacketProps> = ({ packet })
|
||||
icon={<MessageSquareText />}
|
||||
iconBgColor="bg-blue-500"
|
||||
label="Text Message"
|
||||
backgroundColor="bg-blue-950/5"
|
||||
>
|
||||
<div className="max-w-lg bg-neutral-800/30 p-3 rounded-md tracking-tight break-words">
|
||||
{data.textMessage || "Empty message"}
|
||||
|
||||
@@ -40,7 +40,6 @@ export const WaypointPacket: React.FC<WaypointPacketProps> = ({ packet }) => {
|
||||
icon={<MapPin />}
|
||||
iconBgColor="bg-violet-500"
|
||||
label="Waypoint"
|
||||
backgroundColor="bg-violet-950/5"
|
||||
>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div className="space-y-4">
|
||||
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* API client functions for interacting with the Meshstream server
|
||||
*/
|
||||
import { API_ENDPOINTS } from "./config";
|
||||
import { getStreamEndpoint } from "./config";
|
||||
import {
|
||||
Packet,
|
||||
StreamEvent,
|
||||
@@ -204,8 +204,8 @@ export function streamPackets(
|
||||
}
|
||||
|
||||
try {
|
||||
// Create a new EventSource connection
|
||||
source = new EventSource(API_ENDPOINTS.STREAM);
|
||||
// Create a new EventSource connection using dynamic endpoint
|
||||
source = new EventSource(getStreamEndpoint());
|
||||
|
||||
// Log connection attempt
|
||||
if (reconnectAttempt === 0) {
|
||||
|
||||
+9
-15
@@ -12,24 +12,18 @@ export const SITE_TITLE = import.meta.env.VITE_SITE_TITLE || "My Mesh";
|
||||
export const SITE_DESCRIPTION =
|
||||
import.meta.env.VITE_SITE_DESCRIPTION ||
|
||||
"Realtime Meshtastic activity via MQTT.";
|
||||
|
||||
// API URL configuration
|
||||
const getApiBaseUrl = (): string => {
|
||||
// In production, use the same domain (empty string base URL)
|
||||
if (IS_PROD) {
|
||||
return import.meta.env.VITE_API_BASE_URL || "";
|
||||
}
|
||||
|
||||
// In development, use the configured base URL with fallback
|
||||
return import.meta.env.VITE_API_BASE_URL || "http://localhost:8080";
|
||||
};
|
||||
|
||||
export const API_BASE_URL = getApiBaseUrl();
|
||||
export const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || "";
|
||||
export const GOOGLE_MAPS_ID = import.meta.env.VITE_GOOGLE_MAPS_ID || "demo-map-id";
|
||||
export const GOOGLE_MAPS_API_KEY = import.meta.env.VITE_GOOGLE_MAPS_API_KEY || "";
|
||||
|
||||
// API endpoints
|
||||
export const API_ENDPOINTS = {
|
||||
STREAM: `${API_BASE_URL}/api/stream`,
|
||||
};
|
||||
|
||||
// Google Maps configuration
|
||||
export const GOOGLE_MAPS_ID = import.meta.env.VITE_GOOGLE_MAPS_ID || "demo-map-id";
|
||||
/**
|
||||
* Get the API endpoint for the stream
|
||||
*/
|
||||
export function getStreamEndpoint(): string {
|
||||
return API_ENDPOINTS.STREAM;
|
||||
}
|
||||
|
||||
Vendored
+2
-1
@@ -48,7 +48,7 @@ declare namespace google {
|
||||
class InfoWindow {
|
||||
constructor(opts?: InfoWindowOptions);
|
||||
setContent(content: string): void;
|
||||
open(map?: Map, anchor?: Marker): void;
|
||||
open(map?: Map, anchor?: any): void;
|
||||
close(): void;
|
||||
}
|
||||
|
||||
@@ -101,6 +101,7 @@ declare namespace google {
|
||||
|
||||
// Event-related functionality
|
||||
const event: {
|
||||
addListener(instance: object, event: string, listener: (Event) => void): MapsEventListener;
|
||||
/**
|
||||
* Removes the given listener, which should have been returned by
|
||||
* google.maps.event.addListener.
|
||||
|
||||
Reference in New Issue
Block a user