165 lines
4.4 KiB
TypeScript
Raw Normal View History

2024-10-31 19:18:21 +01:00
import {
2024-11-01 15:07:30 +01:00
TabSortState,
2024-10-31 19:18:21 +01:00
Row,
2024-11-01 15:07:30 +01:00
Cell,
2024-10-31 19:18:21 +01:00
Table,
} from "@codex-storage/marketplace-ui-components";
2024-10-10 10:34:59 +02:00
import DottedMap from "dotted-map/without-countries";
2024-11-01 15:07:30 +01:00
import { useRef, useState, useCallback } from "react";
import { ErrorCircleIcon } from "../ErrorCircleIcon/ErrorCircleIcon";
import { PeersIcon } from "../Menu/PeersIcon";
import { PeerCountryCell } from "./PeerCountryCell";
import { SuccessCheckIcon } from "../SuccessCheckIcon/SuccessCheckIcon";
import { useDebug } from "../../hooks/useDebug";
import { getMapJSON } from "dotted-map";
import "./Peers.css";
import { PeerPin, PeerSortFn, PeerUtils } from "./peers.util";
2024-10-10 10:34:59 +02:00
// This function accepts the same arguments as DottedMap in the example above.
const mapJsonString = getMapJSON({ height: 60, grid: "diagonal" });
type CustomCSSProperties = React.CSSProperties & {
2024-11-01 15:07:30 +01:00
"--codex-peers-degrees": number;
2024-10-31 19:18:21 +01:00
};
2024-11-01 15:07:30 +01:00
const throwOnError = true;
2024-10-31 19:18:21 +01:00
2024-11-01 15:07:30 +01:00
export const Peers = () => {
2024-10-31 19:18:21 +01:00
const ips = useRef<Record<string, string>>({});
2024-10-11 17:58:13 +02:00
const [pins, setPins] = useState<[PeerPin, number][]>([]);
2024-11-01 15:07:30 +01:00
const [sortFn, setSortFn] = useState<PeerSortFn | null>(() =>
PeerUtils.sortByBoolean("desc")
2024-10-31 19:18:21 +01:00
);
2024-11-01 15:07:30 +01:00
const { data } = useDebug(throwOnError);
2024-10-11 17:58:13 +02:00
2024-10-31 19:18:21 +01:00
const onPinAdd = useCallback(
({
countryIso,
ip,
...pin
}: PeerPin & { countryIso: string; ip: string }) => {
2024-11-01 15:07:30 +01:00
setPins((val) => PeerUtils.incPin(val, pin));
2024-10-31 19:18:21 +01:00
ips.current[ip] = countryIso;
},
[]
);
2024-10-11 17:58:13 +02:00
// Its safe to re-create the map at each render, because of the
// pre-computation its super fast ⚡️
const map = new DottedMap({ map: JSON.parse(mapJsonString) });
pins.map(([pin, quantity]) =>
map.addPin({
lat: pin.lat,
lng: pin.lng,
svgOptions: { color: "#d6ff79", radius: 0.8 * quantity },
})
);
2024-10-10 10:34:59 +02:00
2024-10-11 17:58:13 +02:00
const svgMap = map.getSVG({
radius: 0.32,
color: "#969696",
2024-10-11 17:58:13 +02:00
shape: "circle",
backgroundColor: "#141414",
2024-10-11 17:58:13 +02:00
});
2024-10-31 19:18:21 +01:00
const onSortByCountry = (state: TabSortState) => {
if (!state) {
setSortFn(null);
return;
}
2024-11-01 15:07:30 +01:00
setSortFn(() => PeerUtils.sortByCountry(state, ips.current));
2024-10-31 19:18:21 +01:00
};
2024-10-11 17:58:13 +02:00
2024-10-31 19:18:21 +01:00
const onSortActive = (state: TabSortState) => {
if (!state) {
setSortFn(null);
return;
}
2024-11-01 15:07:30 +01:00
setSortFn(() => PeerUtils.sortByBoolean(state));
2024-10-31 19:18:21 +01:00
};
const headers = [
["Country", onSortByCountry],
["PeerId"],
["Active", onSortActive],
] satisfies [string, ((state: TabSortState) => void)?][];
const nodes = data?.table?.nodes || [];
const sorted = sortFn ? nodes.slice().sort(sortFn) : nodes;
const rows = sorted.map((node) => (
<Row
cells={[
<PeerCountryCell
onPinAdd={onPinAdd}
address={node.address}></PeerCountryCell>,
<Cell>{node.peerId}</Cell>,
<Cell>
{node.seen ? (
<div className="status--active">
<SuccessCheckIcon variant="primary"></SuccessCheckIcon> Active
</div>
) : (
<div className="status--inactive">
<ErrorCircleIcon></ErrorCircleIcon> Inactive
</div>
)}
</Cell>,
]}></Row>
));
2024-11-01 15:07:30 +01:00
const actives = PeerUtils.countActives(sorted);
const degrees = PeerUtils.calculareDegrees(sorted);
const good = actives > 0;
2024-10-11 17:58:13 +02:00
const styles: CustomCSSProperties = {
2024-11-01 15:07:30 +01:00
"--codex-peers-degrees": degrees,
};
2024-10-11 17:58:13 +02:00
return (
<div className="peers">
<div>
<div dangerouslySetInnerHTML={{ __html: svgMap }}></div>
2024-10-31 19:18:21 +01:00
<div>
<ul>
<li>Legend</li>
<li>1-3</li>
<li>3-5</li>
<li>5 +</li>
</ul>
<div className="connections">
<header>
<PeersIcon></PeersIcon>
<span>Connections</span>
</header>
<main style={styles}>
<div>
<div></div>
<span>{actives}</span>
</div>
</main>
<footer>
{good ? (
<>
<SuccessCheckIcon variant="primary"></SuccessCheckIcon>
<span>Peer connections in good standing. </span>
</>
) : (
<>
<ErrorCircleIcon />
<span>No peer connection active. </span>
</>
)}
</footer>
</div>
</div>
</div>
2024-10-31 19:18:21 +01:00
<Table headers={headers} rows={rows} defaultSortIndex={2} />
2024-10-11 17:58:13 +02:00
</div>
);
};