You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

144 lines
4.4 KiB

import type React from 'react';
import { useEffect, useState } from 'react';
import { m } from 'framer-motion';
import type { Edge, Node } from 'react-flow-renderer';
import ReactFlow, { Background, Controls, MiniMap } from 'react-flow-renderer';
import { BiCrown } from 'react-icons/bi';
import { FiSettings } from 'react-icons/fi';
import { RiMindMap } from 'react-icons/ri';
import { IconButton } from '@components/generic/button/IconButton';
import { Tooltip } from '@components/generic/Tooltip';
import { Layout } from '@components/layout';
import { SidebarItem } from '@components/layout/Sidebar/SidebarItem';
import { Hashicon } from '@emeraldpay/hashicon-react';
import { useAppSelector } from '@hooks/useAppSelector';
export const Nodes = (): JSX.Element => {
const [graphNodes, setGraphNodes] = useState<Node[]>([]);
const [graphEdges, setGraphEdges] = useState<Edge[]>([]);
const [selected, setSelected] = useState<number>(0);
const nodes = useAppSelector((state) => state.meshtastic.nodes);
const myNodeNum = useAppSelector(
(state) => state.meshtastic.radio.hardware.myNodeNum,
);
useEffect(() => {
const tmpNodes: Node[] = [];
// User Terminal
tmpNodes.push({
id: '1',
type: 'input',
data: { label: 'User Terminal' },
position: { x: 160 + 500, y: 0 + 500 },
});
nodes.map((node, index) => {
tmpNodes.push({
id: node.data.num.toString(),
data: { label: node.data.user?.longName ?? `Unknown ${node.data.num}` },
position: { x: index * 160 + 500, y: 100 + 500 },
});
});
setGraphNodes(tmpNodes);
}, [nodes, myNodeNum]);
useEffect(() => {
const tmpEdges: Edge[] = [];
nodes.map((node, index) => {
if (node.data.num === myNodeNum) {
tmpEdges.push({
id: `e${1}-${myNodeNum}`,
source: '1',
target: myNodeNum.toString(),
type: 'smoothstep',
style: {
stroke: 'yellow',
strokeWidth: 2,
},
});
}
// node.routes.map((route) => {
// tmpEdges.push({
// id: `e${route.from}-${route.to}`,
// source: node.num.toString(),
// target: route.to.toString(),
// type: 'smoothstep',
// animated: true,
// });
// });
});
setGraphEdges(tmpEdges);
}, [nodes, myNodeNum]);
return (
<Layout
title="Nodes"
icon={<RiMindMap />}
sidebarContents={
<>
{nodes.map((node) => (
<SidebarItem
key={node.data.num}
selected={node.data.num === selected}
setSelected={(): void => {
setSelected(node.data.num);
}}
actions={
<IconButton
nested
onClick={(e): void => {
e.stopPropagation();
setSelected(node.data.num);
}}
icon={<FiSettings />}
/>
}
>
<div className="flex dark:text-white">
<div className="relative m-auto">
{node.data.num === myNodeNum && (
<Tooltip content="Your Node">
<m.div
whileHover={{ scale: 1.05 }}
className="absolute -right-1 -top-1 rounded-full bg-yellow-500 p-0.5"
>
<BiCrown className="h-3 w-3" />
</m.div>
</Tooltip>
)}
<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.data.lastHeard
? new Date(node.data.lastHeard).toLocaleTimeString(
undefined,
{
hour: '2-digit',
minute: '2-digit',
},
)
: 'Never'}
</div>
</SidebarItem>
))}
</>
}
>
<div className="relative flex h-full w-full">
<ReactFlow nodes={graphNodes} edges={graphEdges}>
<MiniMap />
<Controls />
<Background />
</ReactFlow>
</div>
</Layout>
);
};