Add maps, Bluetooth config & fixes

This commit is contained in:
Sacha Weatherstone
2022-08-19 18:18:48 +09:30
parent b8877f8a63
commit 9f1d3498ea
44 changed files with 611 additions and 1064 deletions
+6 -10
View File
@@ -20,17 +20,17 @@
},
"homepage": "https://meshtastic.org",
"dependencies": {
"@arcgis/core": "^4.24.7",
"@emeraldpay/hashicon-react": "^0.5.2",
"@hookform/resolvers": "^2.9.7",
"@meshtastic/eslint-config": "^1.0.8",
"@meshtastic/meshtasticjs": "^0.6.91",
"@meshtastic/meshtasticjs": "^0.6.92",
"base64-js": "^1.5.1",
"class-transformer": "^0.5.1",
"class-validator": "^0.13.2",
"evergreen-ui": "^6.10.3",
"geodesy": "^2.4.0",
"immer": "^9.0.15",
"maplibre-gl": "^2.3.0",
"modern-css-reset": "^1.4.0",
"prettier": "^2.7.1",
"react": "^18.2.0",
@@ -40,15 +40,12 @@
"react-json-pretty": "^2.2.0",
"react-qrcode-logo": "^2.8.0",
"rfc4648": "^1.5.2",
"snarkdown": "^2.0.0",
"swr": "^1.3.0",
"vite-plugin-environment": "^1.1.2",
"zustand": "4.0.0"
"zustand": "4.1.0"
},
"devDependencies": {
"@types/chrome": "^0.0.193",
"@types/geodesy": "^2.2.3",
"@types/node": "^18.7.5",
"@types/node": "^18.7.6",
"@types/react": "^18.0.17",
"@types/react-dom": "^18.0.6",
"@types/w3c-web-serial": "^1.0.2",
@@ -59,8 +56,7 @@
"tar": "^6.1.11",
"tslib": "^2.4.0",
"typescript": "^4.7.4",
"unimported": "^1.21.0",
"vite": "^3.0.7",
"vite-plugin-cdn-import": "^0.3.5"
"vite": "^3.0.8",
"vite-plugin-environment": "^1.1.2"
}
}
+311 -812
View File
File diff suppressed because it is too large Load Diff
+2 -1
View File
@@ -1,6 +1,7 @@
import type React from "react";
import { Device, DeviceContext } from "./core/stores/deviceStore.js";
import { DeviceContext } from "./core/providers/useDevice.js";
import type { Device } from "./core/stores/deviceStore.js";
export interface DeviceProps {
children: React.ReactNode;
+4 -2
View File
@@ -1,10 +1,12 @@
import type React from "react";
import { useDevice } from "./core/stores/deviceStore.js";
import { useDevice } from "@core/providers/useDevice.js";
import { ChannelsPage } from "./pages/Channels/index.js";
import { ConfigPage } from "./pages/Config/index.js";
import { ExtensionsPage } from "./pages/Extensions/Index.js";
import { InfoPage } from "./pages/Info/index.js";
import { MapPage } from "./pages/Map/index.js";
import { MessagesPage } from "./pages/Messages/index.js";
export const PageRouter = (): JSX.Element => {
@@ -12,7 +14,7 @@ export const PageRouter = (): JSX.Element => {
return (
<>
{activePage === "messages" && <MessagesPage />}
{/* {activePage === "map" && <MapPage />} */}
{activePage === "map" && <MapPage />}
{activePage === "extensions" && <ExtensionsPage />}
{activePage === "config" && <ConfigPage />}
{activePage === "channels" && <ChannelsPage />}
+1 -1
View File
@@ -11,8 +11,8 @@ import {
Tooltip,
} from "evergreen-ui";
import { useDevice } from "@app/core/stores/deviceStore.js";
import { toMGRS } from "@app/core/utils/toMGRS.js";
import { useDevice } from "@core/providers/useDevice.js";
import { Hashicon } from "@emeraldpay/hashicon-react";
import { Protobuf } from "@meshtastic/meshtasticjs";
@@ -0,0 +1,100 @@
import type React from "react";
import { useEffect, useState } from "react";
import { FormField, SelectField, Switch, TextInputField } from "evergreen-ui";
import { Controller, useForm, useWatch } from "react-hook-form";
import { BluetoothValidation } from "@app/validation/config/bluetooth.js";
import { Form } from "@components/form/Form";
import { useDevice } from "@core/providers/useDevice.js";
import { renderOptions } from "@core/utils/selectEnumOptions.js";
import { classValidatorResolver } from "@hookform/resolvers/class-validator";
import { Protobuf } from "@meshtastic/meshtasticjs";
export const Bluetooth = (): JSX.Element => {
const { config, connection } = useDevice();
const [loading, setLoading] = useState(false);
const {
register,
handleSubmit,
formState: { errors, isDirty },
control,
reset,
} = useForm<BluetoothValidation>({
defaultValues: config.bluetooth,
resolver: classValidatorResolver(BluetoothValidation),
});
useEffect(() => {
reset(config.bluetooth);
}, [reset, config.bluetooth]);
const onSubmit = handleSubmit((data) => {
setLoading(true);
void connection?.setConfig(
{
payloadVariant: {
oneofKind: "bluetooth",
bluetooth: data,
},
},
async () => {
reset({ ...data });
setLoading(false);
await Promise.resolve();
}
);
});
const pairingMode = useWatch({
control,
name: "mode",
defaultValue: Protobuf.Config_BluetoothConfig_PairingMode.RandomPin,
});
return (
<Form loading={loading} dirty={isDirty} onSubmit={onSubmit}>
<FormField
label="Bluetooth Enabled"
description="Description"
isInvalid={!!errors.enabled?.message}
validationMessage={errors.enabled?.message}
>
<Controller
name="enabled"
control={control}
render={({ field: { value, ...field } }) => (
<Switch height={24} marginLeft="auto" checked={value} {...field} />
)}
/>
</FormField>
<SelectField
label="Pairing mode"
description="This is a description."
isInvalid={!!errors.mode?.message}
validationMessage={errors.mode?.message}
{...register("mode", { valueAsNumber: true })}
>
{renderOptions(Protobuf.Config_BluetoothConfig_PairingMode)}
</SelectField>
<TextInputField
display={
pairingMode !== Protobuf.Config_BluetoothConfig_PairingMode.FixedPin
? "none"
: "block"
}
label="Pin"
description="This is a description."
type="number"
isInvalid={!!errors.fixedPin?.message}
validationMessage={errors.fixedPin?.message}
{...register("fixedPin", {
valueAsNumber: true,
})}
/>
</Form>
);
};
@@ -12,7 +12,7 @@ import { Controller, useForm } from "react-hook-form";
import { DeviceValidation } from "@app/validation/config/device.js";
import { Form } from "@components/form/Form";
import { useDevice } from "@core/stores/deviceStore.js";
import { useDevice } from "@core/providers/useDevice.js";
import { renderOptions } from "@core/utils/selectEnumOptions.js";
import { classValidatorResolver } from "@hookform/resolvers/class-validator";
import { Protobuf } from "@meshtastic/meshtasticjs";
@@ -6,7 +6,7 @@ import { Controller, useForm } from "react-hook-form";
import { DisplayValidation } from "@app/validation/config/display.js";
import { Form } from "@components/form/Form";
import { useDevice } from "@core/stores/deviceStore.js";
import { useDevice } from "@core/providers/useDevice.js";
import { renderOptions } from "@core/utils/selectEnumOptions.js";
import { classValidatorResolver } from "@hookform/resolvers/class-validator";
import { Protobuf } from "@meshtastic/meshtasticjs";
@@ -6,7 +6,7 @@ import { Controller, useForm } from "react-hook-form";
import { LoRaValidation } from "@app/validation/config/lora.js";
import { Form } from "@components/form/Form";
import { useDevice } from "@core/stores/deviceStore.js";
import { useDevice } from "@core/providers/useDevice.js";
import { renderOptions } from "@core/utils/selectEnumOptions.js";
import { classValidatorResolver } from "@hookform/resolvers/class-validator";
import { Protobuf } from "@meshtastic/meshtasticjs";
@@ -12,7 +12,7 @@ import { Controller, useForm } from "react-hook-form";
import { PositionValidation } from "@app/validation/config/position.js";
import { Form } from "@components/form/Form";
import { useDevice } from "@core/stores/deviceStore.js";
import { useDevice } from "@core/providers/useDevice.js";
import { bitwiseDecode } from "@core/utils/bitwise";
import { classValidatorResolver } from "@hookform/resolvers/class-validator";
import { Protobuf } from "@meshtastic/meshtasticjs";
@@ -6,7 +6,7 @@ import { Controller, useForm } from "react-hook-form";
import { PowerValidation } from "@app/validation/config/power.js";
import { Form } from "@components/form/Form";
import { useDevice } from "@core/stores/deviceStore.js";
import { useDevice } from "@core/providers/useDevice.js";
import { renderOptions } from "@core/utils/selectEnumOptions.js";
import { classValidatorResolver } from "@hookform/resolvers/class-validator";
import { Protobuf } from "@meshtastic/meshtasticjs";
@@ -7,7 +7,7 @@ import { base16 } from "rfc4648";
import { UserValidation } from "@app/validation/config/user.js";
import { Form } from "@components/form/Form";
import { useDevice } from "@core/stores/deviceStore.js";
import { useDevice } from "@core/providers/useDevice.js";
import { renderOptions } from "@core/utils/selectEnumOptions.js";
import { classValidatorResolver } from "@hookform/resolvers/class-validator";
import { Protobuf } from "@meshtastic/meshtasticjs";
@@ -13,7 +13,7 @@ import { Controller, useForm, useWatch } from "react-hook-form";
import { renderOptions } from "@app/core/utils/selectEnumOptions.js";
import { WiFiValidation } from "@app/validation/config/wifi.js";
import { Form } from "@components/form/Form";
import { useDevice } from "@core/stores/deviceStore.js";
import { useDevice } from "@core/providers/useDevice.js";
import { classValidatorResolver } from "@hookform/resolvers/class-validator";
import { Protobuf } from "@meshtastic/meshtasticjs";
@@ -6,7 +6,7 @@ import { Controller, useForm, useWatch } from "react-hook-form";
import { CannedMessageValidation } from "@app/validation/moduleConfig/cannedMessage.js";
import { Form } from "@components/form/Form";
import { useDevice } from "@core/stores/deviceStore.js";
import { useDevice } from "@core/providers/useDevice.js";
import { renderOptions } from "@core/utils/selectEnumOptions.js";
import { classValidatorResolver } from "@hookform/resolvers/class-validator";
import { Protobuf } from "@meshtastic/meshtasticjs";
@@ -6,7 +6,7 @@ import { Controller, useForm, useWatch } from "react-hook-form";
import { ExternalNotificationValidation } from "@app/validation/moduleConfig/externalNotification.js";
import { Form } from "@components/form/Form";
import { useDevice } from "@core/stores/deviceStore.js";
import { useDevice } from "@core/providers/useDevice.js";
import { classValidatorResolver } from "@hookform/resolvers/class-validator";
export const ExternalNotification = (): JSX.Element => {
@@ -6,7 +6,7 @@ import { Controller, useForm, useWatch } from "react-hook-form";
import { MQTTValidation } from "@app/validation/moduleConfig/mqtt.js";
import { Form } from "@components/form/Form";
import { useDevice } from "@core/stores/deviceStore.js";
import { useDevice } from "@core/providers/useDevice.js";
import { classValidatorResolver } from "@hookform/resolvers/class-validator";
export const MQTT = (): JSX.Element => {
@@ -6,7 +6,7 @@ import { Controller, useForm, useWatch } from "react-hook-form";
import { RangeTestValidation } from "@app/validation/moduleConfig/rangeTest.js";
import { Form } from "@components/form/Form";
import { useDevice } from "@core/stores/deviceStore.js";
import { useDevice } from "@core/providers/useDevice.js";
import { classValidatorResolver } from "@hookform/resolvers/class-validator";
export const RangeTest = (): JSX.Element => {
@@ -6,7 +6,7 @@ import { Controller, useForm, useWatch } from "react-hook-form";
import { SerialValidation } from "@app/validation/moduleConfig/serial.js";
import { Form } from "@components/form/Form";
import { useDevice } from "@core/stores/deviceStore.js";
import { useDevice } from "@core/providers/useDevice.js";
import { classValidatorResolver } from "@hookform/resolvers/class-validator";
export const Serial = (): JSX.Element => {
@@ -6,7 +6,7 @@ import { Controller, useForm, useWatch } from "react-hook-form";
import { StoreForwardValidation } from "@app/validation/moduleConfig/storeForward.js";
import { Form } from "@components/form/Form";
import { useDevice } from "@core/stores/deviceStore.js";
import { useDevice } from "@core/providers/useDevice.js";
import { classValidatorResolver } from "@hookform/resolvers/class-validator";
export const StoreForward = (): JSX.Element => {
@@ -6,7 +6,7 @@ import { Controller, useForm } from "react-hook-form";
import { TelemetryValidation } from "@app/validation/moduleConfig/telemetry.js";
import { Form } from "@components/form/Form";
import { useDevice } from "@core/stores/deviceStore.js";
import { useDevice } from "@core/providers/useDevice.js";
import { classValidatorResolver } from "@hookform/resolvers/class-validator";
export const Telemetry = (): JSX.Element => {
+1 -1
View File
@@ -9,7 +9,7 @@ import {
StatusIndicator,
} from "evergreen-ui";
import { useDevice } from "@app/core/stores/deviceStore.js";
import { useDevice } from "@core/providers/useDevice.js";
export const Progress = (): JSX.Element => {
const {
+2 -2
View File
@@ -3,7 +3,8 @@ import { useEffect, useState } from "react";
import { GeolocationIcon, Pane, PropertyIcon, SideSheet } from "evergreen-ui";
import { Node, useDevice } from "@app/core/stores/deviceStore.js";
import { useDevice } from "@core/providers/useDevice.js";
import type { Node } from "@core/stores/deviceStore.js";
import { Hashicon } from "@emeraldpay/hashicon-react";
import { Protobuf } from "@meshtastic/meshtasticjs";
@@ -14,7 +15,6 @@ 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(() => {
+2 -3
View File
@@ -1,8 +1,7 @@
import type React from "react";
import type { HTMLProps } from "react";
import { Button, majorScale, Pane, Spinner } from "evergreen-ui";
import { FiSave } from "react-icons/fi";
import { Button, majorScale, Pane, SavedIcon, Spinner } from "evergreen-ui";
export interface FormProps extends HTMLProps<HTMLFormElement> {
onSubmit: (event: React.FormEvent<HTMLFormElement>) => Promise<void>;
@@ -39,7 +38,7 @@ export const Form = ({
type="submit"
marginLeft="auto"
disabled={!dirty}
iconBefore={<FiSave />}
iconBefore={<SavedIcon />}
>
Save
</Button>
+2 -2
View File
@@ -11,7 +11,7 @@ import {
import { FiBluetooth, FiTerminal, FiWifi } from "react-icons/fi";
import { toMGRS } from "@app/core/utils/toMGRS.js";
import { useDevice } from "@core/stores/deviceStore.js";
import { useDevice } from "@core/providers/useDevice.js";
import { Hashicon } from "@emeraldpay/hashicon-react";
import { Types } from "@meshtastic/meshtasticjs";
@@ -33,7 +33,7 @@ export const DeviceCard = (): JSX.Element => {
<Heading>{myNode?.data.user?.longName}</Heading>
<Link
target="_blank"
href="https://github.com/meshtastic/meshtastic-web/releases/"
href="https://github.com/meshtastic/meshtastic-device/releases/"
>
<Badge
color="green"
+2 -2
View File
@@ -17,7 +17,8 @@ import {
} from "evergreen-ui";
import { PeersDialog } from "@app/components/Dialog/PeersDialog.js";
import { Page, useDevice } from "@app/core/stores/deviceStore.js";
import { useDevice } from "@core/providers/useDevice.js";
import type { Page } from "@core/stores/deviceStore.js";
import { DeviceCard } from "./DeviceCard.js";
@@ -42,7 +43,6 @@ export const Sidebar = (): JSX.Element => {
name: "Map",
icon: GlobeIcon,
page: "map",
disabled: true,
},
{
name: "Extensions",
+32
View File
@@ -0,0 +1,32 @@
import type React from "react";
import { useEffect, useState } from "react";
import { Map, MapOptions } from "maplibre-gl";
export interface useMapboxProps {
ref: React.RefObject<HTMLDivElement>;
style: string;
options?: Partial<MapOptions>;
}
export function useCreateMapbox({
ref,
style,
options,
}: useMapboxProps): Map | undefined {
const [mapInstance, setMapInstance] = useState<Map>();
useEffect(() => {
const container = ref.current as HTMLDivElement;
if (mapInstance || !container) {
return;
}
const map = new Map({
container,
style,
...options,
});
setMapInstance(map);
}, []);
return mapInstance;
}
+13
View File
@@ -0,0 +1,13 @@
import { createContext, useContext } from "react";
import type { Device } from "../stores/deviceStore.js";
export const DeviceContext = createContext<Device | undefined>(undefined);
export const useDevice = (): Device => {
const context = useContext(DeviceContext);
if (context === undefined) {
throw new Error("useDevice must be used within a ConnectionProvider");
}
return context;
};
+14
View File
@@ -0,0 +1,14 @@
import { createContext, useContext } from "react";
import type { Map } from "maplibre-gl";
export interface MapContextValue {
ref: React.Ref<HTMLDivElement>;
map?: Map;
}
export const MapContext = createContext<MapContextValue>({} as MapContextValue);
export const useMap = (): MapContextValue => {
return useContext(MapContext);
};
-7
View File
@@ -1,7 +0,0 @@
export const fetcher = async <JSON>(
input: RequestInfo,
init?: RequestInit
): Promise<JSON> => {
const res = await fetch(input, init);
return res.json() as Promise<JSON>;
};
+1
View File
@@ -1,4 +1,5 @@
import "modern-css-reset/dist/reset.min.css";
import "maplibre-gl/dist/maplibre-gl.css";
import type React from "react";
import { StrictMode } from "react";
+1 -1
View File
@@ -19,7 +19,7 @@ import {
import { Controller, useForm } from "react-hook-form";
import { Form } from "@components/form/Form";
import { useDevice } from "@core/stores/deviceStore.js";
import { useDevice } from "@core/providers/useDevice.js";
import { Protobuf } from "@meshtastic/meshtasticjs";
export interface SettingsPanelProps {
+1 -1
View File
@@ -6,7 +6,7 @@ import { IoQrCodeOutline } from "react-icons/io5";
import { QRDialog } from "@app/components/Dialog/QRDialog.js";
import { TabbedContent, TabType } from "@components/layout/page/TabbedContent";
import { useDevice } from "@core/stores/deviceStore.js";
import { useDevice } from "@core/providers/useDevice.js";
import { Protobuf } from "@meshtastic/meshtasticjs";
import { Channel } from "./Channel.js";
+6 -3
View File
@@ -3,7 +3,7 @@ import { useState } from "react";
import { Pane, Tab, Tablist } from "evergreen-ui";
import { useDevice } from "@app/core/stores/deviceStore.js";
import { Bluetooth } from "@app/components/PageComponents/Config/Bluetooth.js";
import { Device } from "@components/PageComponents/Config/Device.js";
import { Display } from "@components/PageComponents/Config/Display.js";
import { LoRa } from "@components/PageComponents/Config/LoRa.js";
@@ -11,6 +11,7 @@ import { Position } from "@components/PageComponents/Config/Position.js";
import { Power } from "@components/PageComponents/Config/Power.js";
import { User } from "@components/PageComponents/Config/User.js";
import { WiFi } from "@components/PageComponents/Config/WiFi.js";
import { useDevice } from "@core/providers/useDevice.js";
export const DeviceConfig = (): JSX.Element => {
const [selectedIndex, setSelectedIndex] = useState(0);
@@ -46,8 +47,10 @@ export const DeviceConfig = (): JSX.Element => {
label: "LoRa",
element: LoRa,
},
// Channels
// Interface
{
label: "Bluetooth",
element: Bluetooth,
},
];
return (
+1 -1
View File
@@ -2,7 +2,7 @@ import type React from "react";
import { Pane } from "evergreen-ui";
import { useDevice } from "@app/core/stores/deviceStore.js";
import { useDevice } from "@core/providers/useDevice.js";
export const Environment = (): JSX.Element => {
const { nodes } = useDevice();
+10 -7
View File
@@ -1,9 +1,7 @@
import type React from "react";
import { useEffect, useState } from "react";
import { Pane } from "evergreen-ui";
import useSWR from "swr";
import { fetcher } from "@core/utils/fetcher";
export interface File {
nameModified: string;
@@ -24,10 +22,15 @@ export interface Files {
}
export const FileBrowser = (): JSX.Element => {
const { data } = useSWR<Files>(
"http://meshtastic.local/json/fs/browse/static",
fetcher
);
const [data, setData] = useState<Files>();
useEffect(() => {
void fetch("http://meshtastic.local/json/fs/browse/static").then(
async (res) => {
setData((await res.json()) as Files);
}
);
});
return (
<Pane>
+1 -1
View File
@@ -2,8 +2,8 @@ import type React from "react";
import { DocumentIcon, GanttChartIcon, RainIcon } from "evergreen-ui";
import { useDevice } from "@app/core/stores/deviceStore.js";
import { TabbedContent, TabType } from "@components/layout/page/TabbedContent";
import { useDevice } from "@core/providers/useDevice.js";
import { FileBrowser } from "@pages/Extensions/FileBrowser";
import { Environment } from "./Environment.js";
+1 -1
View File
@@ -3,7 +3,7 @@ import type React from "react";
import { Pane } from "evergreen-ui";
import JSONPretty from "react-json-pretty";
import { useDevice } from "@app/core/stores/deviceStore.js";
import { useDevice } from "@core/providers/useDevice.js";
export const InfoPage = (): JSX.Element => {
const { hardware, nodes } = useDevice();
+49 -123
View File
@@ -1,137 +1,63 @@
import type React from "react";
import { useEffect, useMemo, useRef } from "react";
import { Pane } from "evergreen-ui";
import { majorScale, Pane } from "evergreen-ui";
import { Marker } from "maplibre-gl";
import { useDevice } from "@app/core/stores/deviceStore.js";
import Point from "@arcgis/core/geometry/Point";
import Graphic from "@arcgis/core/Graphic";
import FeatureLayer from "@arcgis/core/layers/FeatureLayer";
import LabelClass from "@arcgis/core/layers/support/LabelClass";
import Map from "@arcgis/core/Map";
import LineCallout3D from "@arcgis/core/symbols/callouts/LineCallout3D";
import LabelSymbol3D from "@arcgis/core/symbols/LabelSymbol3D";
import TextSymbol3DLayer from "@arcgis/core/symbols/TextSymbol3DLayer";
import SceneView from "@arcgis/core/views/SceneView";
import { useCreateMapbox } from "@app/core/providers/useCreateMapbox.js";
import { useDevice } from "@core/providers/useDevice.js";
export const MapPage = (): JSX.Element => {
const { nodes } = useDevice();
const nodesWithPosition = nodes.filter((node) => node.data.position);
const nodeMarkers = useMemo(() => new Map<number, Marker>(), []);
const ref = useRef<HTMLDivElement>(null);
useEffect(() => {
console.log(nodesWithPosition);
}, [nodesWithPosition]);
const labelClass = useMemo(
() =>
new LabelClass({
labelExpressionInfo: {
expression: "$feature.name",
},
symbol: new LabelSymbol3D({
symbolLayers: [
new TextSymbol3DLayer({
text: "{name}",
material: {
color: "black",
},
halo: {
color: [255, 255, 255, 0.7],
size: 2,
},
font: {
size: 12,
weight: "bold",
},
size: 10,
}),
],
verticalOffset: {
screenLength: 150,
maxWorldLength: 2000,
minWorldLength: 30,
},
callout: new LineCallout3D({
size: 0.5,
color: [0, 0, 0],
border: {
color: [255, 255, 255],
},
}),
}),
}),
[]
);
const points: Graphic[] = nodesWithPosition.map(
(node, index) =>
node.data.position
? new Graphic({
geometry: new Point({
latitude: node.data.position.latitudeI / 1e7,
longitude: node.data.position.longitudeI / 1e7,
}),
attributes: {
ObjectID: index,
name: node.data.user?.longName,
},
})
: new Graphic() //should be undefined/removed from array
);
const map = useCreateMapbox({
ref,
style:
"https://raw.githubusercontent.com/hc-oss/maplibre-gl-styles/master/styles/osm-mapnik/v8/default.json",
});
useEffect(() => {
if (ref.current) {
const layer = new FeatureLayer({
labelsVisible: true,
labelingInfo: [labelClass],
source: points,
fields: [
{
name: "ObjectID",
alias: "ObjectID",
type: "oid",
},
{
name: "name",
alias: "Name",
type: "string",
},
],
});
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]);
const map = new Map({
basemap: "satellite",
ground: "world-elevation",
layers: [layer],
});
const scene = new SceneView({
container: ref.current,
map: map,
camera: {
position: nodesWithPosition[0]
? {
x: nodesWithPosition[0].data.position?.longitudeI ?? 0 / 1e7,
y: nodesWithPosition[0].data.position?.latitudeI ?? 0 / 1e7,
z: nodesWithPosition[0].data.position?.altitude ?? 0 / 1e7,
}
: {
y: -35.59, //Longitude
x: 148, //Latitude
z: 200, //Meters
},
tilt: 75,
},
});
scene.on("click", (event) => {
void scene.hitTest(event).then((point) => {
console.log(point);
});
});
}
}, [labelClass, points]);
return <Pane width="100%" height="100%" ref={ref} />;
return (
<Pane
margin={majorScale(3)}
borderRadius={majorScale(1)}
background="white"
elevation={1}
display="flex"
flexGrow={1}
flexDirection="column"
gap={majorScale(2)}
overflow="hidden"
>
<Pane width="100%" height="100%" ref={ref} />
</Pane>
);
};
+1 -1
View File
@@ -12,8 +12,8 @@ import {
Tooltip,
} from "evergreen-ui";
import { useDevice } from "@core/providers/useDevice.js";
import type { Channel } from "@core/stores/deviceStore.js";
import { useDevice } from "@core/stores/deviceStore.js";
import { Message } from "./Message.js";
import { NewLocationMessage } from "./NewLocationMessage.js";
+1 -1
View File
@@ -10,7 +10,7 @@ import {
Text,
} from "evergreen-ui";
import { useDevice } from "@app/core/stores/deviceStore.js";
import { useDevice } from "@core/providers/useDevice.js";
import { Hashicon } from "@emeraldpay/hashicon-react";
import type { Protobuf, Types } from "@meshtastic/meshtasticjs";
+1 -1
View File
@@ -8,8 +8,8 @@ import {
TextInputField,
} from "evergreen-ui";
import { useDevice } from "@app/core/stores/deviceStore.js";
import { renderOptions } from "@app/core/utils/selectEnumOptions.js";
import { useDevice } from "@core/providers/useDevice.js";
import { Protobuf } from "@meshtastic/meshtasticjs";
enum LocationType {
+1 -1
View File
@@ -13,7 +13,7 @@ import {
TabbedContent,
TabType,
} from "@components/layout/page/TabbedContent.js";
import { useDevice } from "@core/stores/deviceStore.js";
import { useDevice } from "@core/providers/useDevice.js";
import { Protobuf } from "@meshtastic/meshtasticjs";
import { ChannelChat } from "./ChannelChat.js";
+14
View File
@@ -0,0 +1,14 @@
import { IsBoolean, IsEnum, IsInt } from "class-validator";
import { Protobuf } from "@meshtastic/meshtasticjs";
export class BluetoothValidation implements Protobuf.Config_BluetoothConfig {
@IsBoolean()
enabled: boolean;
@IsEnum(Protobuf.Config_BluetoothConfig_PairingMode)
mode: Protobuf.Config_BluetoothConfig_PairingMode;
@IsInt()
fixedPin: number;
}
+16 -65
View File
@@ -1,18 +1,17 @@
import { execSync } from 'child_process';
import { resolve } from 'path';
import { visualizer } from 'rollup-plugin-visualizer';
import { defineConfig } from 'vite';
import importToCDN from 'vite-plugin-cdn-import';
import EnvironmentPlugin from 'vite-plugin-environment';
import { execSync } from "child_process";
import { resolve } from "path";
import { visualizer } from "rollup-plugin-visualizer";
import { defineConfig } from "vite";
import EnvironmentPlugin from "vite-plugin-environment";
import react from '@vitejs/plugin-react';
import react from "@vitejs/plugin-react";
let hash = '';
let hash = "";
try {
hash = execSync('git rev-parse --short HEAD').toString().trim();
hash = execSync("git rev-parse --short HEAD").toString().trim();
} catch (error) {
hash = 'DEVELOPMENT';
hash = "DEVELOPMENT";
}
export default defineConfig({
@@ -21,69 +20,21 @@ export default defineConfig({
EnvironmentPlugin({
COMMIT_HASH: hash,
}),
importToCDN({
modules: [
// {
// name: 'mapbox-gl',
// var: 'mapboxgl',
// path: `dist/mapbox-gl.js`,
// },
// autoComplete('@arcgis/core'),
],
}),
// VitePWA({
// mode: 'production',
// includeAssets: [
// 'favicon.svg',
// 'favicon.ico',
// 'robots.txt',
// 'touch-icon.png',
// ],
// manifest: {
// name: 'Meshtastic Web',
// short_name: 'Meshtastic',
// description: 'Meshtastic Web App',
// theme_color: '#67ea94',
// icons: [
// {
// src: 'android-192.png',
// sizes: '192x192',
// type: 'image/png',
// },
// {
// src: 'android-512.png',
// sizes: '512x512',
// type: 'image/png',
// },
// {
// src: 'android-512.png',
// sizes: '512x512',
// type: 'image/png',
// purpose: 'any maskable',
// },
// ],
// },
// workbox: {
// sourcemap: true,
// },
// }),
],
build: {
target: 'esnext',
assetsDir: '',
target: "esnext",
assetsDir: "",
rollupOptions: {
plugins: [visualizer()],
},
},
resolve: {
alias: {
'@app': resolve(__dirname, './src'),
'@pages': resolve(__dirname, './src/pages'),
'@components': resolve(__dirname, './src/components'),
'@core': resolve(__dirname, './src/core'),
'@layouts': resolve(__dirname, './src/layouts'),
"@app": resolve(__dirname, "./src"),
"@pages": resolve(__dirname, "./src/pages"),
"@components": resolve(__dirname, "./src/components"),
"@core": resolve(__dirname, "./src/core"),
"@layouts": resolve(__dirname, "./src/layouts"),
},
},
});