Updates for telemetry rework

This commit is contained in:
Sacha Weatherstone
2022-04-13 14:29:24 +10:00
parent f0c561a854
commit d84d7d94cc
16 changed files with 994 additions and 1000 deletions

View File

@@ -21,51 +21,51 @@
"homepage": "https://meshtastic.org",
"dependencies": {
"@emeraldpay/hashicon-react": "^0.5.2",
"@meshtastic/eslint-config": "^1.0.7",
"@meshtastic/meshtasticjs": "^0.6.54",
"@reduxjs/toolkit": "^1.8.0",
"@meshtastic/eslint-config": "^1.0.8",
"@meshtastic/meshtasticjs": "^0.6.55",
"@reduxjs/toolkit": "^1.8.1",
"@tippyjs/react": "^4.2.6",
"base64-js": "^1.5.1",
"framer-motion": "^6.2.8",
"mapbox-gl": "^2.7.1",
"prettier": "^2.6.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"framer-motion": "^6.2.10",
"mapbox-gl": "^2.8.0",
"prettier": "^2.6.2",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"react-error-boundary": "^3.1.4",
"react-flow-renderer": "^10.0.6",
"react-hook-form": "^7.28.1",
"react-flow-renderer": "^10.1.0",
"react-hook-form": "^7.29.0",
"react-icons": "^4.3.1",
"react-json-pretty": "^2.2.0",
"react-multi-select-component": "^4.2.3",
"react-redux": "^7.2.6",
"react-use-clipboard": "^1.0.7",
"react-multi-select-component": "^4.2.4",
"react-redux": "^7.2.8",
"react-use-clipboard": "^1.0.8",
"rfc4648": "^1.5.1",
"swr": "^1.2.2",
"swr": "^1.3.0",
"timeago-react": "^3.0.4",
"tippy.js": "^6.3.7",
"type-route": "^0.6.0",
"vite-plugin-environment": "^1.1.0"
"type-route": "^0.7.1",
"vite-plugin-environment": "^1.1.1"
},
"devDependencies": {
"@hookform/devtools": "^4.0.2",
"@types/chrome": "^0.0.180",
"@types/mapbox-gl": "^2.6.3",
"@types/react": "^17.0.43",
"@types/react-dom": "^17.0.14",
"@hookform/devtools": "^4.1.0",
"@types/chrome": "^0.0.181",
"@types/mapbox-gl": "^2.6.4",
"@types/react": "^18.0.3",
"@types/react-dom": "^18.0.0",
"@types/w3c-web-serial": "^1.0.2",
"@types/web-bluetooth": "^0.0.14",
"@vitejs/plugin-react": "^1.2.0",
"@vitejs/plugin-react": "^1.3.0",
"autoprefixer": "^10.4.4",
"gzipper": "^7.0.0",
"gzipper": "^7.1.0",
"postcss": "^8.4.12",
"rollup-plugin-visualizer": "^5.6.0",
"tailwindcss": "^3.0.23",
"tailwindcss": "^3.0.24",
"tar": "^6.1.11",
"typescript": "^4.6.3",
"unimported": "^1.19.1",
"vite": "^2.8.6",
"vite": "^2.9.1",
"vite-plugin-cdn-import": "^0.3.5",
"vite-plugin-pwa": "^0.11.13",
"workbox-window": "^6.5.2"
"workbox-window": "^6.5.3"
}
}

1655
pnpm-lock.yaml generated
View File

File diff suppressed because it is too large Load Diff

View File

@@ -34,6 +34,7 @@ export const SidebarOverlay = ({
}
transition={{ type: 'just' }}
>
{/* @ts-expect-error */}
<AnimateSharedLayout>
{/* <div className="flex gap-2 border-b border-gray-400 p-2 dark:border-gray-600"> */}
<div className="bg-white px-1 pt-1 drop-shadow-md dark:bg-primaryDark">

View File

@@ -47,12 +47,6 @@ export const LoRa = (): JSX.Element => {
optionsEnum={Protobuf.Role}
{...register('role')}
/>
<Input
label="Send Owner Interval"
type="number"
suffix="Seconds"
{...register('sendOwnerInterval', { valueAsNumber: true })}
/>
<Input
label="Frequency Offset"
type="number"

View File

@@ -18,7 +18,7 @@ export const User = (): JSX.Element => {
(state) => state.meshtastic.radio.hardware,
).myNodeNum;
const node = useAppSelector((state) => state.meshtastic.nodes).find(
(node) => node.num === myNodeNum,
(node) => node.data.num === myNodeNum,
);
const { register, handleSubmit, formState, reset } = useForm<{
longName: string;
@@ -30,51 +30,47 @@ export const User = (): JSX.Element => {
txPowerDbm: number;
}>({
defaultValues: {
longName: node?.user?.longName,
shortName: node?.user?.shortName,
isLicensed: node?.user?.isLicensed,
team: node?.user?.team,
antAzimuth: node?.user?.antAzimuth,
antGainDbi: node?.user?.antGainDbi,
txPowerDbm: node?.user?.txPowerDbm,
longName: node?.data.user?.longName,
shortName: node?.data.user?.shortName,
isLicensed: node?.data.user?.isLicensed,
team: node?.data.user?.team,
antAzimuth: node?.data.user?.antAzimuth,
antGainDbi: node?.data.user?.antGainDbi,
txPowerDbm: node?.data.user?.txPowerDbm,
},
});
useEffect(() => {
reset({
longName: node?.user?.longName,
shortName: node?.user?.shortName,
isLicensed: node?.user?.isLicensed,
team: node?.user?.team,
longName: node?.data.user?.longName,
shortName: node?.data.user?.shortName,
isLicensed: node?.data.user?.isLicensed,
team: node?.data.user?.team,
});
}, [reset, node]);
const onSubmit = handleSubmit((data) => {
setLoading(true);
if (node?.user) {
void connection.setOwner({ ...node.user, ...data }, async () => {
if (node?.data.user) {
void connection.setOwner({ ...node.data.user, ...data }, async () => {
reset({ ...data });
setLoading(false);
await Promise.resolve();
});
// TODO: can be removed once getUser is implemented
// dispatch(
// addUser({ ...node.user, ...{ data: { ...node.user.data, ...data } } }),
// );
}
});
return (
<Form loading={loading} dirty={!formState.isDirty} submit={onSubmit}>
<Input label="Device ID" value={node?.user?.id} disabled />
<Input label="Device ID" value={node?.data.user?.id} disabled />
<Input label="Device Name" {...register('longName')} />
<Input label="Short Name" maxLength={3} {...register('shortName')} />
<Input
label="Mac Address"
defaultValue={
base16
.stringify(node?.user?.macaddr ?? [])
.stringify(node?.data.user?.macaddr ?? [])
.match(/.{1,2}/g)
?.join(':') ?? ''
}
@@ -84,7 +80,7 @@ export const User = (): JSX.Element => {
label="Hardware (DEPRECATED)"
value={
Protobuf.HardwareModel[
node?.user?.hwModel ?? Protobuf.HardwareModel.UNSET
node?.data.user?.hwModel ?? Protobuf.HardwareModel.UNSET
]
}
disabled

View File

@@ -50,41 +50,42 @@ export const CannedMessage = (): JSX.Element => {
{...register('rotary1Enabled')}
/>
<Input
label="Encoder #1 Pin A"
label="Encoder Pin A"
type="number"
disabled={moduleEnabled}
{...register('rotary1PinA', { valueAsNumber: true })}
{...register('inputbrokerPinA', { valueAsNumber: true })}
/>
<Input
label="Encoder #1 Pin B"
label="Encoder Pin B"
type="number"
disabled={moduleEnabled}
{...register('rotary1PinB', { valueAsNumber: true })}
{...register('inputbrokerPinB', { valueAsNumber: true })}
/>
<Input
label="Endoer #1 Pin Press"
label="Endoer Pin Press"
type="number"
disabled={moduleEnabled}
{...register('rotary1PinPress', { valueAsNumber: true })}
{...register('inputbrokerPinPress', { valueAsNumber: true })}
/>
<Select
label="Clockwise event"
disabled={moduleEnabled}
optionsEnum={Protobuf.InputEventChar}
{...register('rotary1EventCw', { valueAsNumber: true })}
{...register('inputbrokerEventCw', { valueAsNumber: true })}
/>
<Select
label="Counter Clockwise event"
disabled={moduleEnabled}
optionsEnum={Protobuf.InputEventChar}
{...register('rotary1EventCcw', { valueAsNumber: true })}
{...register('inputbrokerEventCcw', { valueAsNumber: true })}
/>
<Select
label="Press event"
disabled={moduleEnabled}
optionsEnum={Protobuf.InputEventChar}
{...register('rotary1EventPress', { valueAsNumber: true })}
{...register('inputbrokerEventPress', { valueAsNumber: true })}
/>
<Checkbox label="Up Down enabled" {...register('updown1Enabled')} />
<Input
label="Allow Input Source"
disabled={moduleEnabled}

View File

@@ -37,16 +37,16 @@ export const Telemetry = (): JSX.Element => {
<Form loading={loading} dirty={!formState.isDirty} submit={onSubmit}>
<Checkbox
label="Measurement Enabled"
{...register('telemetryModuleMeasurementEnabled')}
{...register('telemetryModuleEnvironmentMeasurementEnabled')}
/>
<Checkbox
label="Displayed on Screen"
{...register('telemetryModuleScreenEnabled')}
{...register('telemetryModuleEnvironmentScreenEnabled')}
/>
<Input
label="Read Error Count Threshold"
type="number"
{...register('telemetryModuleReadErrorCountThreshold', {
{...register('telemetryModuleEnvironmentReadErrorCountThreshold', {
valueAsNumber: true,
})}
/>
@@ -54,7 +54,7 @@ export const Telemetry = (): JSX.Element => {
label="Update Interval"
suffix="Seconds"
type="number"
{...register('telemetryModuleUpdateInterval', {
{...register('telemetryModuleEnvironmentUpdateInterval', {
valueAsNumber: true,
})}
/>
@@ -62,23 +62,27 @@ export const Telemetry = (): JSX.Element => {
label="Recovery Interval"
suffix="Seconds"
type="number"
{...register('telemetryModuleRecoveryInterval', {
{...register('telemetryModuleEnvironmentRecoveryInterval', {
valueAsNumber: true,
})}
/>
<Checkbox
label="Display Farenheit"
{...register('telemetryModuleDisplayFahrenheit')}
{...register('telemetryModuleEnvironmentDisplayFahrenheit')}
/>
<Select
label="Sensor Type"
optionsEnum={Protobuf.RadioConfig_UserPreferences_TelemetrySensorType}
{...register('telemetryModuleSensorType', { valueAsNumber: true })}
{...register('telemetryModuleEnvironmentSensorType', {
valueAsNumber: true,
})}
/>
<Input
label="Sensor Pin"
type="number"
{...register('telemetryModuleSensorPin', { valueAsNumber: true })}
{...register('telemetryModuleEnvironmentSensorPin', {
valueAsNumber: true,
})}
/>
</Form>
);

View File

@@ -5,15 +5,18 @@ import {
FiBluetooth,
FiCpu,
FiGitBranch,
FiHexagon,
FiMenu,
FiMoon,
FiSun,
FiWifi,
FiX,
} from 'react-icons/fi';
import {
IoBatteryChargingOutline,
IoBatteryDeadOutline,
IoBatteryFullOutline,
} from 'react-icons/io5';
import { MdUpgrade } from 'react-icons/md';
import { AiOutlineHeart, AiFillHeart } from 'react-icons/ai';
import {
RiArrowDownLine,
RiArrowUpDownLine,
@@ -31,13 +34,6 @@ import {
import { useAppDispatch } from '@hooks/useAppDispatch';
import { useAppSelector } from '@hooks/useAppSelector';
import { Protobuf, Types } from '@meshtastic/meshtasticjs';
import {
IoBatteryChargingOutline,
IoBatteryDeadOutline,
IoBatteryFullOutline,
IoBatteryHalfOutline,
} from 'react-icons/io5';
import { FaTrafficLight } from 'react-icons/fa';
export const BottomNav = (): JSX.Element => {
const [showVersionInfo, setShowVersionInfo] = useState(false);
@@ -47,8 +43,8 @@ export const BottomNav = (): JSX.Element => {
const primaryChannelSettings = useAppSelector(
(state) => state.meshtastic.radio.channels,
).find((channel) => channel.role === Protobuf.Channel_Role.PRIMARY)?.settings;
const telemetry =
meshtasticState.nodes[meshtasticState.radio.hardware.myNodeNum]?.telemetry;
const metrics =
meshtasticState.nodes[meshtasticState.radio.hardware.myNodeNum]?.metrics;
return (
<div className="z-20 flex justify-between divide-x divide-gray-400 border-t border-gray-400 bg-white dark:divide-gray-600 dark:border-gray-600 dark:bg-secondaryDark">
@@ -89,32 +85,26 @@ export const BottomNav = (): JSX.Element => {
)}
<div className="truncate text-xs font-medium">
{meshtasticState.nodes.find(
(node) => node.num === meshtasticState.radio.hardware.myNodeNum,
)?.user?.longName ?? 'Disconnected'}
(node) =>
node.data.num === meshtasticState.radio.hardware.myNodeNum,
)?.data.user?.longName ?? 'Disconnected'}
</div>
</BottomNavItem>
<BottomNavItem tooltip="Router Heartbeat">
{telemetry?.routerHeartbeat ? (
<AiFillHeart className="h-4" />
) : (
<AiOutlineHeart className="h-4" />
)}
</BottomNavItem>
<BottomNavItem tooltip="Battery Level">
{!telemetry?.batteryLevel ? (
{!metrics?.batteryLevel ? (
<IoBatteryDeadOutline className="h-4" />
) : telemetry?.batteryLevel > 50 ? (
) : metrics?.batteryLevel > 50 ? (
<IoBatteryFullOutline className="h-4" />
) : telemetry?.batteryLevel > 0 ? (
) : metrics?.batteryLevel > 0 ? (
<IoBatteryFullOutline className="h-4" />
) : (
<IoBatteryChargingOutline className="h-4" />
)}
<div className="truncate text-xs font-medium">
{telemetry?.batteryLevel
? `${telemetry?.batteryLevel}% - ${telemetry?.voltage}v`
{metrics?.batteryLevel
? `${metrics?.batteryLevel}% - ${metrics?.voltage}v`
: 'No Battery'}
</div>
</BottomNavItem>
@@ -122,22 +112,22 @@ export const BottomNav = (): JSX.Element => {
<BottomNavItem tooltip="Network Utilization">
<div className="m-auto h-3 w-3 rounded-full bg-primary" />
<div className="truncate text-xs font-medium">
{`${telemetry?.airUtilTx ?? 0}% - Air`} |
{`${metrics?.airUtilTx ?? 0}% - Air`} |
</div>
<div
className={`m-auto h-3 w-3 rounded-full ${
!telemetry?.channelUtilization
!metrics?.channelUtilization
? 'bg-primary'
: telemetry?.channelUtilization > 50
: metrics?.channelUtilization > 50
? 'bg-red-400'
: telemetry?.channelUtilization > 24
: metrics?.channelUtilization > 24
? 'bg-yellow-400'
: 'bg-primary'
}`}
/>
<div className="truncate text-xs font-medium">
{`${telemetry?.channelUtilization ?? 0}% - Ch`}
{`${metrics?.channelUtilization ?? 0}% - Ch`}
</div>
</BottomNavItem>

View File

@@ -13,14 +13,6 @@ export interface Chat {
messages: MessageWithAck[];
}
interface CurrentPosition {
latitudeI: number;
longitudeI: number;
altitude: number;
posTimestamp: number;
satsInView: number;
}
type ChatEntries = {
[key in number]: Chat;
};
@@ -33,6 +25,11 @@ interface Route {
//speed stats?
}
export interface Node {
metrics: Protobuf.DeviceMetrics;
data: Protobuf.NodeInfo;
}
export interface Radio {
channels: Protobuf.Channel[];
preferences: Protobuf.RadioConfig_UserPreferences;
@@ -43,7 +40,7 @@ interface MeshtasticState {
deviceStatus: Types.DeviceStatusEnum;
lastMeshInterraction: number;
ready: boolean;
nodes: Protobuf.NodeInfo[];
nodes: Node[];
radio: Radio;
chats: ChatEntries;
logs: Types.LogEventPacket[];
@@ -87,46 +84,56 @@ export const meshtasticSlice = createSlice({
},
addUser: (state, action: PayloadAction<Types.UserPacket>) => {
const node = state.nodes.find(
(node) => node.num === action.payload.packet.from,
(node) => node.data.num === action.payload.packet.from,
);
if (node) {
node.user = action.payload.data;
node.data.user = action.payload.data;
if (action.payload.packet.rxTime) {
node.lastHeard = new Date(
node.data.lastHeard = new Date(
action.payload.packet.rxTime * 1000,
).getTime();
}
} else {
state.nodes.push({
num: action.payload.packet.from,
snr: action.payload.packet.rxSnr,
lastHeard: new Date().getTime(),
...action.payload.packet,
data: {
num: action.payload.packet.from,
snr: action.payload.packet.rxSnr,
lastHeard: new Date().getTime(),
...action.payload.packet,
},
metrics: Protobuf.DeviceMetrics.create(),
});
}
},
addPosition: (state, action: PayloadAction<Types.PositionPacket>) => {
const node = state.nodes.find(
(node) => node.num === action.payload.packet.from,
(node) => node.data.num === action.payload.packet.from,
);
if (node) {
node.position = action.payload.data;
node.data.position = action.payload.data;
if (action.payload.packet.rxTime) {
node.lastHeard = new Date(
node.data.lastHeard = new Date(
action.payload.packet.rxTime * 1000,
).getTime();
}
}
},
addNode: (state, action: PayloadAction<Protobuf.NodeInfo>) => {
const node = state.nodes.find((node) => node.num === action.payload.num);
const node = state.nodes.find(
(node) => node.data.num === action.payload.num,
);
if (node) {
node.lastHeard = new Date(action.payload.lastHeard * 1000).getTime();
node.snr = action.payload.snr;
node.data.lastHeard = new Date(
action.payload.lastHeard * 1000,
).getTime();
node.data.snr = action.payload.snr;
} else {
state.nodes.push(action.payload);
state.nodes.push({
data: action.payload,
metrics: Protobuf.DeviceMetrics.create(),
});
}
},
addChannel: (state, action: PayloadAction<Protobuf.Channel>) => {
@@ -205,9 +212,11 @@ export const meshtasticSlice = createSlice({
state,
action: PayloadAction<{ id: number; time: Date }>,
) => {
const node = state.nodes.find((node) => node.num === action.payload.id);
const node = state.nodes.find(
(node) => node.data.num === action.payload.id,
);
if (node) {
node.lastHeard = action.payload.time.getTime();
node.data.lastHeard = action.payload.time.getTime();
}
},
addChat: (state, action: PayloadAction<number>) => {

View File

@@ -2,9 +2,9 @@ import '@app/index.css';
import type React from 'react';
import { StrictMode } from 'react';
import { render } from 'react-dom';
import { domAnimation, LazyMotion } from 'framer-motion';
import { createRoot } from 'react-dom/client';
import { ErrorBoundary } from 'react-error-boundary';
import { Provider } from 'react-redux';
@@ -14,7 +14,9 @@ import { ReloadPrompt } from '@components/pwa/ReloadPrompt';
import { RouteProvider } from '@core/router';
import { store } from '@core/store';
render(
const container = document.getElementById('root') as HTMLElement;
const root = createRoot(container);
root.render(
<StrictMode>
<ErrorBoundary FallbackComponent={ErrorFallback}>
<RouteProvider>
@@ -27,5 +29,4 @@ render(
</RouteProvider>
</ErrorBoundary>
</StrictMode>,
document.getElementById('root'),
);

View File

@@ -10,7 +10,9 @@ export const Debug = (): JSX.Element => {
(state) => state.meshtastic.radio.hardware,
);
const node = useAppSelector((state) =>
state.meshtastic.nodes.find((node) => node.num === hardwareInfo.myNodeNum),
state.meshtastic.nodes.find(
(node) => node.data.num === hardwareInfo.myNodeNum,
),
);
return (

View File

@@ -14,7 +14,9 @@ export const Info = (): JSX.Element => {
(state) => state.meshtastic.radio.hardware,
);
const node = useAppSelector((state) =>
state.meshtastic.nodes.find((node) => node.num === hardwareInfo.myNodeNum),
state.meshtastic.nodes.find(
(node) => node.data.num === hardwareInfo.myNodeNum,
),
);
return (
@@ -27,7 +29,7 @@ export const Info = (): JSX.Element => {
<div className="m-auto flex flex-col gap-2">
<Hashicon value={hardwareInfo.myNodeNum.toString()} size={180} />
<div className="text-center text-lg font-medium dark:text-white">
{node?.user?.longName || 'Unknown'}
{node?.data.user?.longName || 'Unknown'}
</div>
</div>
{/* <img

View File

@@ -8,10 +8,10 @@ import { RiRoadMapLine } from 'react-icons/ri';
import { Layout } from '@components/layout';
import { MapboxProvider } from '@components/MapBox/MapboxProvider';
import { useAppSelector } from '@hooks/useAppSelector';
import type { Protobuf } from '@meshtastic/meshtasticjs';
import { MapContainer } from '@pages/Map/MapContainer';
import { Marker } from '@pages/Map/Marker';
import { NodeCard } from '@pages/Nodes/NodeCard';
import type { Protobuf } from '@meshtastic/meshtasticjs';
export const Map = (): JSX.Element => {
const [selectedNode, setSelectedNode] = useState<Protobuf.NodeInfo>();
@@ -25,22 +25,22 @@ export const Map = (): JSX.Element => {
<MapboxProvider>
{nodes.map((node) => {
return (
node.position && (
node.data.position && (
<Marker
key={node.num}
key={node.data.num}
center={
new mapboxgl.LngLat(
node.position.longitudeI / 1e7,
node.position.latitudeI / 1e7,
node.data.position.longitudeI / 1e7,
node.data.position.latitudeI / 1e7,
)
}
>
<div
onClick={(): void => {
setSelectedNode(node);
setSelectedNode(node.data);
}}
className={`z-50 rounded-full border-2 bg-opacity-30 ${
node.num === selectedNode?.num
node.data.num === selectedNode?.num
? 'border-green-500 bg-green-500'
: 'border-blue-500 bg-blue-500'
}`}
@@ -66,12 +66,12 @@ export const Map = (): JSX.Element => {
{nodes.map((node) => (
<NodeCard
key={node.num}
node={node}
isMyNode={node.num === myNodeNum}
selected={selectedNode?.num === node.num}
key={node.data.num}
node={node.data}
isMyNode={node.data.num === myNodeNum}
selected={selectedNode?.num === node.data.num}
setSelected={(): void => {
setSelectedNode(node);
setSelectedNode(node.data);
}}
/>
))}

View File

@@ -27,7 +27,7 @@ export const ChannelChat = ({
(state) => state.meshtastic.radio.hardware,
).myNodeNum;
const nodes = useAppSelector((state) => state.meshtastic.nodes).filter(
(node) => node.num !== myNodeNum,
(node) => node.data.num !== myNodeNum,
);
const chats = useAppSelector((state) => state.meshtastic.chats);
const channels = useAppSelector(
@@ -80,7 +80,7 @@ export const ChannelChat = ({
<Tooltip
key={nodeId}
content={
nodes.find((node) => node.num === nodeId)?.user
nodes.find((node) => node.data.num === nodeId)?.data.user
?.longName ?? 'UNK'
}
>

View File

@@ -52,11 +52,11 @@ export const Messages = (): JSX.Element => {
<div className="mx-2 border-b border-gray-400 dark:border-gray-600" />
)}
{nodes
.filter((node) => node.num !== myNodeNum)
.filter((node) => node.data.num !== myNodeNum)
.map((node) => (
<DmChat
key={node.num}
node={node}
key={node.data.num}
node={node.data}
selectedIndex={selectedChatIndex}
setSelectedIndex={setSelectedChatIndex}
/>
@@ -81,8 +81,8 @@ export const Messages = (): JSX.Element => {
</span>
) : (
<span className="text-gray-500 dark:text-gray-400">
{nodes.find((node) => node.num === selectedChatIndex)?.user
?.longName ?? 'Unknown'}
{nodes.find((node) => node.data.num === selectedChatIndex)
?.data.user?.longName ?? 'Unknown'}
</span>
)}
</div>
@@ -105,9 +105,11 @@ export const Messages = (): JSX.Element => {
: chats[selectedChatIndex].messages[index - 1].message
.packet.from === message.message.packet.from
}
sender={nodes.find(
(node) => node.num === message.message.packet.from,
)}
sender={
nodes.find(
(node) => node.data.num === message.message.packet.from,
)?.data
}
/>
))}
</div>

View File

@@ -37,8 +37,8 @@ export const Nodes = (): JSX.Element => {
nodes.map((node, index) => {
tmpNodes.push({
id: node.num.toString(),
data: { label: node.user?.longName ?? `Unknown ${node.num}` },
id: node.data.num.toString(),
data: { label: node.data.user?.longName ?? `Unknown ${node.data.num}` },
position: { x: index * 160 + 500, y: 100 + 500 },
});
});
@@ -50,7 +50,7 @@ export const Nodes = (): JSX.Element => {
const tmpEdges: Edge[] = [];
nodes.map((node, index) => {
if (node.num === myNodeNum) {
if (node.data.num === myNodeNum) {
tmpEdges.push({
id: `e${1}-${myNodeNum}`,
source: '1',
@@ -85,17 +85,17 @@ export const Nodes = (): JSX.Element => {
<>
{nodes.map((node) => (
<SidebarItem
key={node.num}
selected={node.num === selected}
key={node.data.num}
selected={node.data.num === selected}
setSelected={(): void => {
setSelected(node.num);
setSelected(node.data.num);
}}
actions={
<IconButton
nested
onClick={(e): void => {
e.stopPropagation();
setSelected(node.num);
setSelected(node.data.num);
}}
icon={<FiSettings />}
/>
@@ -103,7 +103,7 @@ export const Nodes = (): JSX.Element => {
>
<div className="flex dark:text-white">
<div className="relative m-auto">
{node.num === myNodeNum && (
{node.data.num === myNodeNum && (
<Tooltip content="Your Node">
<m.div
whileHover={{ scale: 1.05 }}
@@ -113,15 +113,18 @@ export const Nodes = (): JSX.Element => {
</m.div>
</Tooltip>
)}
<Hashicon value={node.num.toString()} size={32} />
<Hashicon value={node.data.num.toString()} size={32} />
</div>
</div>
<div className="my-auto mr-auto text-xs font-semibold dark:text-gray-400">
{node.lastHeard
? new Date(node.lastHeard).toLocaleTimeString(undefined, {
hour: '2-digit',
minute: '2-digit',
})
{node.data.lastHeard
? new Date(node.data.lastHeard).toLocaleTimeString(
undefined,
{
hour: '2-digit',
minute: '2-digit',
},
)
: 'Never'}
</div>
</SidebarItem>